/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Diff of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by wakaba, Wed May 2 13:44:34 2007 UTC revision 1.124 by wakaba, Sun Apr 6 10:34:11 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4    use Error qw(:try);
5    
6  ## This is an early version of an HTML parser.  ## ISSUE:
7    ## var doc = implementation.createDocument (null, null, null);
8    ## doc.write ('');
9    ## alert (doc.compatMode);
10    
11    ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)
12    ## TODO: 1252 parse error (revision 1264)
13    ## TODO: 8859-11 = 874 (revision 1271)
14    
15    sub A_EL () { 0b1 }
16    sub ADDRESS_EL () { 0b10 }
17    sub BODY_EL () { 0b100 }
18    sub BUTTON_EL () { 0b1000 }
19    sub CAPTION_EL () { 0b10000 }
20    sub DD_EL () { 0b100000 }
21    sub DIV_EL () { 0b1000000 }
22    sub DT_EL () { 0b10000000 }
23    sub FORM_EL () { 0b100000000 }
24    sub FORMATTING_EL () { 0b1000000000 }
25    sub FRAMESET_EL () { 0b10000000000 }
26    sub HEADING_EL () { 0b100000000000 }
27    sub HTML_EL () { 0b1000000000000 }
28    sub LI_EL () { 0b10000000000000 }
29    sub NOBR_EL () { 0b100000000000000 }
30    sub OPTION_EL () { 0b1000000000000000 }
31    sub OPTGROUP_EL () { 0b10000000000000000 }
32    sub P_EL () { 0b100000000000000000 }
33    sub SELECT_EL () { 0b1000000000000000000 }
34    sub TABLE_EL () { 0b10000000000000000000 }
35    sub TABLE_CELL_EL () { 0b100000000000000000000 }
36    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
37    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
38    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
39    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
40    
41    sub TABLE_ROWS_EL () {
42      TABLE_EL |
43      TABLE_ROW_EL |
44      TABLE_ROW_GROUP_EL
45    }
46    
47    sub END_TAG_OPTIONAL_EL () {
48      DD_EL |
49      DT_EL |
50      LI_EL |
51      P_EL
52    }
53    
54    sub ALL_END_TAG_OPTIONAL_EL () {
55      END_TAG_OPTIONAL_EL |
56      BODY_EL |
57      HTML_EL |
58      TABLE_CELL_EL |
59      TABLE_ROW_EL |
60      TABLE_ROW_GROUP_EL
61    }
62    
63    sub SCOPING_EL () {
64      BUTTON_EL |
65      CAPTION_EL |
66      HTML_EL |
67      TABLE_EL |
68      TABLE_CELL_EL |
69      MISC_SCOPING_EL
70    }
71    
72    sub TABLE_SCOPING_EL () {
73      HTML_EL |
74      TABLE_EL
75    }
76    
77    sub TABLE_ROWS_SCOPING_EL () {
78      HTML_EL |
79      TABLE_ROW_GROUP_EL
80    }
81    
82    sub TABLE_ROW_SCOPING_EL () {
83      HTML_EL |
84      TABLE_ROW_EL
85    }
86    
87    sub SPECIAL_EL () {
88      ADDRESS_EL |
89      BODY_EL |
90      DIV_EL |
91      END_TAG_OPTIONAL_EL |
92      FORM_EL |
93      FRAMESET_EL |
94      HEADING_EL |
95      OPTION_EL |
96      OPTGROUP_EL |
97      SELECT_EL |
98      TABLE_ROW_EL |
99      TABLE_ROW_GROUP_EL |
100      MISC_SPECIAL_EL
101    }
102    
103    my $el_category = {
104      a => A_EL | FORMATTING_EL,
105      address => ADDRESS_EL,
106      applet => MISC_SCOPING_EL,
107      area => MISC_SPECIAL_EL,
108      b => FORMATTING_EL,
109      base => MISC_SPECIAL_EL,
110      basefont => MISC_SPECIAL_EL,
111      bgsound => MISC_SPECIAL_EL,
112      big => FORMATTING_EL,
113      blockquote => MISC_SPECIAL_EL,
114      body => BODY_EL,
115      br => MISC_SPECIAL_EL,
116      button => BUTTON_EL,
117      caption => CAPTION_EL,
118      center => MISC_SPECIAL_EL,
119      col => MISC_SPECIAL_EL,
120      colgroup => MISC_SPECIAL_EL,
121      dd => DD_EL,
122      dir => MISC_SPECIAL_EL,
123      div => DIV_EL,
124      dl => MISC_SPECIAL_EL,
125      dt => DT_EL,
126      em => FORMATTING_EL,
127      embed => MISC_SPECIAL_EL,
128      fieldset => MISC_SPECIAL_EL,
129      font => FORMATTING_EL,
130      form => FORM_EL,
131      frame => MISC_SPECIAL_EL,
132      frameset => FRAMESET_EL,
133      h1 => HEADING_EL,
134      h2 => HEADING_EL,
135      h3 => HEADING_EL,
136      h4 => HEADING_EL,
137      h5 => HEADING_EL,
138      h6 => HEADING_EL,
139      head => MISC_SPECIAL_EL,
140      hr => MISC_SPECIAL_EL,
141      html => HTML_EL,
142      i => FORMATTING_EL,
143      iframe => MISC_SPECIAL_EL,
144      img => MISC_SPECIAL_EL,
145      input => MISC_SPECIAL_EL,
146      isindex => MISC_SPECIAL_EL,
147      li => LI_EL,
148      link => MISC_SPECIAL_EL,
149      listing => MISC_SPECIAL_EL,
150      marquee => MISC_SCOPING_EL,
151      menu => MISC_SPECIAL_EL,
152      meta => MISC_SPECIAL_EL,
153      nobr => NOBR_EL | FORMATTING_EL,
154      noembed => MISC_SPECIAL_EL,
155      noframes => MISC_SPECIAL_EL,
156      noscript => MISC_SPECIAL_EL,
157      object => MISC_SCOPING_EL,
158      ol => MISC_SPECIAL_EL,
159      optgroup => OPTGROUP_EL,
160      option => OPTION_EL,
161      p => P_EL,
162      param => MISC_SPECIAL_EL,
163      plaintext => MISC_SPECIAL_EL,
164      pre => MISC_SPECIAL_EL,
165      s => FORMATTING_EL,
166      script => MISC_SPECIAL_EL,
167      select => SELECT_EL,
168      small => FORMATTING_EL,
169      spacer => MISC_SPECIAL_EL,
170      strike => FORMATTING_EL,
171      strong => FORMATTING_EL,
172      style => MISC_SPECIAL_EL,
173      table => TABLE_EL,
174      tbody => TABLE_ROW_GROUP_EL,
175      td => TABLE_CELL_EL,
176      textarea => MISC_SPECIAL_EL,
177      tfoot => TABLE_ROW_GROUP_EL,
178      th => TABLE_CELL_EL,
179      thead => TABLE_ROW_GROUP_EL,
180      title => MISC_SPECIAL_EL,
181      tr => TABLE_ROW_EL,
182      tt => FORMATTING_EL,
183      u => FORMATTING_EL,
184      ul => MISC_SPECIAL_EL,
185      wbr => MISC_SPECIAL_EL,
186    };
187    
188  my $permitted_slash_tag_name = {  my $permitted_slash_tag_name = {
189    base => 1,    base => 1,
# Line 10  my $permitted_slash_tag_name = { Line 191  my $permitted_slash_tag_name = {
191    meta => 1,    meta => 1,
192    hr => 1,    hr => 1,
193    br => 1,    br => 1,
194    img=> 1,    img => 1,
195    embed => 1,    embed => 1,
196    param => 1,    param => 1,
197    area => 1,    area => 1,
# Line 18  my $permitted_slash_tag_name = { Line 199  my $permitted_slash_tag_name = {
199    input => 1,    input => 1,
200  };  };
201    
202  my $entity_char = {  my $c1_entity_char = {
203    AElig => "\x{00C6}",    0x80 => 0x20AC,
204    Aacute => "\x{00C1}",    0x81 => 0xFFFD,
205    Acirc => "\x{00C2}",    0x82 => 0x201A,
206    Agrave => "\x{00C0}",    0x83 => 0x0192,
207    Alpha => "\x{0391}",    0x84 => 0x201E,
208    Aring => "\x{00C5}",    0x85 => 0x2026,
209    Atilde => "\x{00C3}",    0x86 => 0x2020,
210    Auml => "\x{00C4}",    0x87 => 0x2021,
211    Beta => "\x{0392}",    0x88 => 0x02C6,
212    Ccedil => "\x{00C7}",    0x89 => 0x2030,
213    Chi => "\x{03A7}",    0x8A => 0x0160,
214    Dagger => "\x{2021}",    0x8B => 0x2039,
215    Delta => "\x{0394}",    0x8C => 0x0152,
216    ETH => "\x{00D0}",    0x8D => 0xFFFD,
217    Eacute => "\x{00C9}",    0x8E => 0x017D,
218    Ecirc => "\x{00CA}",    0x8F => 0xFFFD,
219    Egrave => "\x{00C8}",    0x90 => 0xFFFD,
220    Epsilon => "\x{0395}",    0x91 => 0x2018,
221    Eta => "\x{0397}",    0x92 => 0x2019,
222    Euml => "\x{00CB}",    0x93 => 0x201C,
223    Gamma => "\x{0393}",    0x94 => 0x201D,
224    Iacute => "\x{00CD}",    0x95 => 0x2022,
225    Icirc => "\x{00CE}",    0x96 => 0x2013,
226    Igrave => "\x{00CC}",    0x97 => 0x2014,
227    Iota => "\x{0399}",    0x98 => 0x02DC,
228    Iuml => "\x{00CF}",    0x99 => 0x2122,
229    Kappa => "\x{039A}",    0x9A => 0x0161,
230    Lambda => "\x{039B}",    0x9B => 0x203A,
231    Mu => "\x{039C}",    0x9C => 0x0153,
232    Ntilde => "\x{00D1}",    0x9D => 0xFFFD,
233    Nu => "\x{039D}",    0x9E => 0x017E,
234    OElig => "\x{0152}",    0x9F => 0x0178,
235    Oacute => "\x{00D3}",  }; # $c1_entity_char
236    Ocirc => "\x{00D4}",  
237    Ograve => "\x{00D2}",  sub parse_byte_string ($$$$;$) {
238    Omega => "\x{03A9}",    my $self = ref $_[0] ? shift : shift->new;
239    Omicron => "\x{039F}",    my $charset = shift;
240    Oslash => "\x{00D8}",    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);
241    Otilde => "\x{00D5}",    my $s;
242    Ouml => "\x{00D6}",    
243    Phi => "\x{03A6}",    if (defined $charset) {
244    Pi => "\x{03A0}",      require Encode; ## TODO: decode(utf8) don't delete BOM
245    Prime => "\x{2033}",      $s = \ (Encode::decode ($charset, $$bytes_s));
246    Psi => "\x{03A8}",      $self->{input_encoding} = lc $charset; ## TODO: normalize name
247    Rho => "\x{03A1}",      $self->{confident} = 1;
248    Scaron => "\x{0160}",    } else {
249    Sigma => "\x{03A3}",      ## TODO: Implement HTML5 detection algorithm
250    THORN => "\x{00DE}",      require Whatpm::Charset::UniversalCharDet;
251    Tau => "\x{03A4}",      $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string
252    Theta => "\x{0398}",          (substr ($$bytes_s, 0, 1024));
253    Uacute => "\x{00DA}",      $charset ||= 'windows-1252';
254    Ucirc => "\x{00DB}",      $s = \ (Encode::decode ($charset, $$bytes_s));
255    Ugrave => "\x{00D9}",      $self->{input_encoding} = $charset;
256    Upsilon => "\x{03A5}",      $self->{confident} = 0;
257    Uuml => "\x{00DC}",    }
   Xi => "\x{039E}",  
   Yacute => "\x{00DD}",  
   Yuml => "\x{0178}",  
   Zeta => "\x{0396}",  
   aacute => "\x{00E1}",  
   acirc => "\x{00E2}",  
   acute => "\x{00B4}",  
   aelig => "\x{00E6}",  
   agrave => "\x{00E0}",  
   alefsym => "\x{2135}",  
   alpha => "\x{03B1}",  
   amp => "\x{0026}",  
   AMP => "\x{0026}",  
   and => "\x{2227}",  
   ang => "\x{2220}",  
   apos => "\x{0027}",  
   aring => "\x{00E5}",  
   asymp => "\x{2248}",  
   atilde => "\x{00E3}",  
   auml => "\x{00E4}",  
   bdquo => "\x{201E}",  
   beta => "\x{03B2}",  
   brvbar => "\x{00A6}",  
   bull => "\x{2022}",  
   cap => "\x{2229}",  
   ccedil => "\x{00E7}",  
   cedil => "\x{00B8}",  
   cent => "\x{00A2}",  
   chi => "\x{03C7}",  
   circ => "\x{02C6}",  
   clubs => "\x{2663}",  
   cong => "\x{2245}",  
   copy => "\x{00A9}",  
   COPY => "\x{00A9}",  
   crarr => "\x{21B5}",  
   cup => "\x{222A}",  
   curren => "\x{00A4}",  
   dArr => "\x{21D3}",  
   dagger => "\x{2020}",  
   darr => "\x{2193}",  
   deg => "\x{00B0}",  
   delta => "\x{03B4}",  
   diams => "\x{2666}",  
   divide => "\x{00F7}",  
   eacute => "\x{00E9}",  
   ecirc => "\x{00EA}",  
   egrave => "\x{00E8}",  
   empty => "\x{2205}",  
   emsp => "\x{2003}",  
   ensp => "\x{2002}",  
   epsilon => "\x{03B5}",  
   equiv => "\x{2261}",  
   eta => "\x{03B7}",  
   eth => "\x{00F0}",  
   euml => "\x{00EB}",  
   euro => "\x{20AC}",  
   exist => "\x{2203}",  
   fnof => "\x{0192}",  
   forall => "\x{2200}",  
   frac12 => "\x{00BD}",  
   frac14 => "\x{00BC}",  
   frac34 => "\x{00BE}",  
   frasl => "\x{2044}",  
   gamma => "\x{03B3}",  
   ge => "\x{2265}",  
   gt => "\x{003E}",  
   GT => "\x{003E}",  
   hArr => "\x{21D4}",  
   harr => "\x{2194}",  
   hearts => "\x{2665}",  
   hellip => "\x{2026}",  
   iacute => "\x{00ED}",  
   icirc => "\x{00EE}",  
   iexcl => "\x{00A1}",  
   igrave => "\x{00EC}",  
   image => "\x{2111}",  
   infin => "\x{221E}",  
   int => "\x{222B}",  
   iota => "\x{03B9}",  
   iquest => "\x{00BF}",  
   isin => "\x{2208}",  
   iuml => "\x{00EF}",  
   kappa => "\x{03BA}",  
   lArr => "\x{21D0}",  
   lambda => "\x{03BB}",  
   lang => "\x{2329}",  
   laquo => "\x{00AB}",  
   larr => "\x{2190}",  
   lceil => "\x{2308}",  
   ldquo => "\x{201C}",  
   le => "\x{2264}",  
   lfloor => "\x{230A}",  
   lowast => "\x{2217}",  
   loz => "\x{25CA}",  
   lrm => "\x{200E}",  
   lsaquo => "\x{2039}",  
   lsquo => "\x{2018}",  
   lt => "\x{003C}",  
   LT => "\x{003C}",  
   macr => "\x{00AF}",  
   mdash => "\x{2014}",  
   micro => "\x{00B5}",  
   middot => "\x{00B7}",  
   minus => "\x{2212}",  
   mu => "\x{03BC}",  
   nabla => "\x{2207}",  
   nbsp => "\x{00A0}",  
   ndash => "\x{2013}",  
   ne => "\x{2260}",  
   ni => "\x{220B}",  
   not => "\x{00AC}",  
   notin => "\x{2209}",  
   nsub => "\x{2284}",  
   ntilde => "\x{00F1}",  
   nu => "\x{03BD}",  
   oacute => "\x{00F3}",  
   ocirc => "\x{00F4}",  
   oelig => "\x{0153}",  
   ograve => "\x{00F2}",  
   oline => "\x{203E}",  
   omega => "\x{03C9}",  
   omicron => "\x{03BF}",  
   oplus => "\x{2295}",  
   or => "\x{2228}",  
   ordf => "\x{00AA}",  
   ordm => "\x{00BA}",  
   oslash => "\x{00F8}",  
   otilde => "\x{00F5}",  
   otimes => "\x{2297}",  
   ouml => "\x{00F6}",  
   para => "\x{00B6}",  
   part => "\x{2202}",  
   permil => "\x{2030}",  
   perp => "\x{22A5}",  
   phi => "\x{03C6}",  
   pi => "\x{03C0}",  
   piv => "\x{03D6}",  
   plusmn => "\x{00B1}",  
   pound => "\x{00A3}",  
   prime => "\x{2032}",  
   prod => "\x{220F}",  
   prop => "\x{221D}",  
   psi => "\x{03C8}",  
   quot => "\x{0022}",  
   QUOT => "\x{0022}",  
   rArr => "\x{21D2}",  
   radic => "\x{221A}",  
   rang => "\x{232A}",  
   raquo => "\x{00BB}",  
   rarr => "\x{2192}",  
   rceil => "\x{2309}",  
   rdquo => "\x{201D}",  
   real => "\x{211C}",  
   reg => "\x{00AE}",  
   REG => "\x{00AE}",  
   rfloor => "\x{230B}",  
   rho => "\x{03C1}",  
   rlm => "\x{200F}",  
   rsaquo => "\x{203A}",  
   rsquo => "\x{2019}",  
   sbquo => "\x{201A}",  
   scaron => "\x{0161}",  
   sdot => "\x{22C5}",  
   sect => "\x{00A7}",  
   shy => "\x{00AD}",  
   sigma => "\x{03C3}",  
   sigmaf => "\x{03C2}",  
   sim => "\x{223C}",  
   spades => "\x{2660}",  
   sub => "\x{2282}",  
   sube => "\x{2286}",  
   sum => "\x{2211}",  
   sup => "\x{2283}",  
   sup1 => "\x{00B9}",  
   sup2 => "\x{00B2}",  
   sup3 => "\x{00B3}",  
   supe => "\x{2287}",  
   szlig => "\x{00DF}",  
   tau => "\x{03C4}",  
   there4 => "\x{2234}",  
   theta => "\x{03B8}",  
   thetasym => "\x{03D1}",  
   thinsp => "\x{2009}",  
   thorn => "\x{00FE}",  
   tilde => "\x{02DC}",  
   times => "\x{00D7}",  
   trade => "\x{2122}",  
   uArr => "\x{21D1}",  
   uacute => "\x{00FA}",  
   uarr => "\x{2191}",  
   ucirc => "\x{00FB}",  
   ugrave => "\x{00F9}",  
   uml => "\x{00A8}",  
   upsih => "\x{03D2}",  
   upsilon => "\x{03C5}",  
   uuml => "\x{00FC}",  
   weierp => "\x{2118}",  
   xi => "\x{03BE}",  
   yacute => "\x{00FD}",  
   yen => "\x{00A5}",  
   yuml => "\x{00FF}",  
   zeta => "\x{03B6}",  
   zwj => "\x{200D}",  
   zwnj => "\x{200C}",  
 };  
258    
259  my $special_category = {    $self->{change_encoding} = sub {
260    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,      my $self = shift;
261    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,      my $charset = lc shift;
262    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,      my $token = shift;
263    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,      ## TODO: if $charset is supported
264    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,      ## TODO: normalize charset name
265    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
266    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,      ## "Change the encoding" algorithm:
267    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
268    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,      ## Step 1    
269    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?
270  };        $charset = 'utf-8';
271  my $scoping_category = {      }
272    button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
273    table => 1, td => 1, th => 1,      ## Step 2
274  };      if (defined $self->{input_encoding} and
275  my $formatting_category = {          $self->{input_encoding} eq $charset) {
276    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,        $self->{confident} = 1;
277    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,        return;
278  };      }
279  # $phrasing_category: all other elements  
280        !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.
281            ':'.$charset, level => 'w', token => $token);
282    
283        ## Step 3
284        # if (can) {
285          ## change the encoding on the fly.
286          #$self->{confident} = 1;
287          #return;
288        # }
289    
290        ## Step 4
291        throw Whatpm::HTML::RestartParser (charset => $charset);
292      }; # $self->{change_encoding}
293    
294      my @args = @_; shift @args; # $s
295      my $return;
296      try {
297        $return = $self->parse_char_string ($s, @args);  
298      } catch Whatpm::HTML::RestartParser with {
299        my $charset = shift->{charset};
300        $s = \ (Encode::decode ($charset, $$bytes_s));    
301        $self->{input_encoding} = $charset; ## TODO: normalize
302        $self->{confident} = 1;
303        $return = $self->parse_char_string ($s, @args);
304      };
305      return $return;
306    } # parse_byte_string
307    
308    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
309    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
310    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
311    ## because the core part of our HTML parser expects a string of character,
312    ## not a string of bytes or code units or anything which might contain a BOM.
313    ## Therefore, any parser interface that accepts a string of bytes,
314    ## such as |parse_byte_string| in this module, must ensure that it does
315    ## strip the BOM and never strip any ZWNBSP.
316    
317    *parse_char_string = \&parse_string;
318    
319  sub parse_string ($$$;$) {  sub parse_string ($$$;$) {
320    my $self = shift->new;    my $self = ref $_[0] ? shift : shift->new;
321    my $s = \$_[0];    my $s = ref $_[0] ? $_[0] : \($_[0]);
322    $self->{document} = $_[1];    $self->{document} = $_[1];
323      @{$self->{document}->child_nodes} = ();
324    
325    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
326    
327      $self->{confident} = 1 unless exists $self->{confident};
328      $self->{document}->input_encoding ($self->{input_encoding})
329          if defined $self->{input_encoding};
330    
331    my $i = 0;    my $i = 0;
332    my $line = 1;    $self->{line_prev} = $self->{line} = 1;
333    my $column = 0;    $self->{column_prev} = $self->{column} = 0;
334    $self->{set_next_input_character} = sub {    $self->{set_next_char} = sub {
335      my $self = shift;      my $self = shift;
336      $self->{next_input_character} = -1 and return if $i >= length $$s;  
337      $self->{next_input_character} = ord substr $$s, $i++, 1;      pop @{$self->{prev_char}};
338      $column++;      unshift @{$self->{prev_char}}, $self->{next_char};
339    
340        $self->{next_char} = -1 and return if $i >= length $$s;
341        $self->{next_char} = ord substr $$s, $i++, 1;
342    
343        ($self->{line_prev}, $self->{column_prev})
344            = ($self->{line}, $self->{column});
345        $self->{column}++;
346            
347      if ($self->{next_input_character} == 0x000D) { # CR      if ($self->{next_char} == 0x000A) { # LF
348        if ($i >= length $$s) {        $self->{line}++;
349          #        $self->{column} = 0;
350        } else {      } elsif ($self->{next_char} == 0x000D) { # CR
351          my $next_char = ord substr $$s, $i++, 1;        $i++ if substr ($$s, $i, 1) eq "\x0A";
352          if ($next_char == 0x000A) { # LF        $self->{next_char} = 0x000A; # LF # MUST
353            #        $self->{line}++;
354          } else {        $self->{column} = 0;
355            push @{$self->{char}}, $next_char;      } elsif ($self->{next_char} > 0x10FFFF) {
356          }        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
357        }      } elsif ($self->{next_char} == 0x0000) { # NULL
358        $self->{next_input_character} = 0x000A; # LF # MUST        !!!parse-error (type => 'NULL');
359        $line++;        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
       $column = -1;  
     } elsif ($self->{next_input_character} > 0x10FFFF) {  
       $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
     } elsif ($self->{next_input_character} == 0x0000) { # NULL  
       $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
360      }      }
361    };    };
362      $self->{prev_char} = [-1, -1, -1];
363      $self->{next_char} = -1;
364    
365    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
366      my (%opt) = @_;      my (%opt) = @_;
367      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
368        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
369        warn "Parse error ($opt{type}) at line $line column $column\n";
370    };    };
371    $self->{parse_error} = sub {    $self->{parse_error} = sub {
372      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
373    };    };
374    
375    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 352  sub parse_string ($$$;$) { Line 377  sub parse_string ($$$;$) {
377    $self->_construct_tree;    $self->_construct_tree;
378    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
379    
380      delete $self->{parse_error}; # remove loop
381    
382    return $self->{document};    return $self->{document};
383  } # parse_string  } # parse_string
384    
385  sub new ($) {  sub new ($) {
386    my $class = shift;    my $class = shift;
387    my $self = bless {}, $class;    my $self = bless {}, $class;
388    $self->{set_next_input_character} = sub {    $self->{set_next_char} = sub {
389      $self->{next_input_character} = -1;      $self->{next_char} = -1;
390    };    };
391    $self->{parse_error} = sub {    $self->{parse_error} = sub {
392      #      #
393    };    };
394      $self->{change_encoding} = sub {
395        # if ($_[0] is a supported encoding) {
396        #   run "change the encoding" algorithm;
397        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
398        # }
399      };
400      $self->{application_cache_selection} = sub {
401        #
402      };
403    return $self;    return $self;
404  } # new  } # new
405    
406    sub CM_ENTITY () { 0b001 } # & markup in data
407    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
408    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
409    
410    sub PLAINTEXT_CONTENT_MODEL () { 0 }
411    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
412    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
413    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
414    
415    sub DATA_STATE () { 0 }
416    sub ENTITY_DATA_STATE () { 1 }
417    sub TAG_OPEN_STATE () { 2 }
418    sub CLOSE_TAG_OPEN_STATE () { 3 }
419    sub TAG_NAME_STATE () { 4 }
420    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
421    sub ATTRIBUTE_NAME_STATE () { 6 }
422    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
423    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
424    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
425    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
426    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
427    sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
428    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
429    sub COMMENT_START_STATE () { 14 }
430    sub COMMENT_START_DASH_STATE () { 15 }
431    sub COMMENT_STATE () { 16 }
432    sub COMMENT_END_STATE () { 17 }
433    sub COMMENT_END_DASH_STATE () { 18 }
434    sub BOGUS_COMMENT_STATE () { 19 }
435    sub DOCTYPE_STATE () { 20 }
436    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
437    sub DOCTYPE_NAME_STATE () { 22 }
438    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
439    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
440    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
441    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
442    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
443    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
444    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
445    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
446    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
447    sub BOGUS_DOCTYPE_STATE () { 32 }
448    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
449    
450    sub DOCTYPE_TOKEN () { 1 }
451    sub COMMENT_TOKEN () { 2 }
452    sub START_TAG_TOKEN () { 3 }
453    sub END_TAG_TOKEN () { 4 }
454    sub END_OF_FILE_TOKEN () { 5 }
455    sub CHARACTER_TOKEN () { 6 }
456    
457    sub AFTER_HTML_IMS () { 0b100 }
458    sub HEAD_IMS ()       { 0b1000 }
459    sub BODY_IMS ()       { 0b10000 }
460    sub BODY_TABLE_IMS () { 0b100000 }
461    sub TABLE_IMS ()      { 0b1000000 }
462    sub ROW_IMS ()        { 0b10000000 }
463    sub BODY_AFTER_IMS () { 0b100000000 }
464    sub FRAME_IMS ()      { 0b1000000000 }
465    sub SELECT_IMS ()     { 0b10000000000 }
466    
467    ## NOTE: "initial" and "before html" insertion modes have no constants.
468    
469    ## NOTE: "after after body" insertion mode.
470    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
471    
472    ## NOTE: "after after frameset" insertion mode.
473    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
474    
475    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
476    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
477    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
478    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
479    sub IN_BODY_IM () { BODY_IMS }
480    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
481    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
482    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
483    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
484    sub IN_TABLE_IM () { TABLE_IMS }
485    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
486    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
487    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
488    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
489    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
490    sub IN_COLUMN_GROUP_IM () { 0b10 }
491    
492  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
493    
494  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
495    my $self = shift;    my $self = shift;
496    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
497    $self->{content_model_flag} = 'PCDATA'; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
498    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE
499    undef $self->{current_attribute};    undef $self->{current_attribute};
500    undef $self->{last_emitted_start_tag_name};    undef $self->{last_emitted_start_tag_name};
501    undef $self->{last_attribute_value_state};    undef $self->{last_attribute_value_state};
502    $self->{char} = [];    $self->{char} = [];
503    # $self->{next_input_character}    # $self->{next_char}
504    !!!next-input-character;    !!!next-input-character;
505    $self->{token} = [];    $self->{token} = [];
506      # $self->{escape}
507  } # _initialize_tokenizer  } # _initialize_tokenizer
508    
509  ## A token has:  ## A token has:
510  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
511  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
512  ##   ->{name} (DOCTYPE, start tag (tagname), end tag (tagname))  ##   ->{name} (DOCTYPE_TOKEN)
513      ## ISSUE: the spec need s/tagname/tag name/  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
514  ##   ->{error} == 1 or 0 (DOCTYPE)  ##   ->{public_identifier} (DOCTYPE_TOKEN)
515  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{system_identifier} (DOCTYPE_TOKEN)
516  ##   ->{data} (comment, character)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
517    ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
518  ## Macros  ##        ->{name}
519  ##   Macros MUST be preceded by three EXCLAMATION MARKs.  ##        ->{value}
520  ##   emit ($token)  ##        ->{has_reference} == 1 or 0
521  ##     Emits the specified token.  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
522    
523  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
524    
# Line 405  sub _initialize_tokenizer ($) { Line 528  sub _initialize_tokenizer ($) {
528  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
529  ## and removed from the list.  ## and removed from the list.
530    
531    ## NOTE: HTML5 "Writing HTML documents" section, applied to
532    ## documents and not to user agents and conformance checkers,
533    ## contains some requirements that are not detected by the
534    ## parsing algorithm:
535    ## - Some requirements on character encoding declarations. ## TODO
536    ## - "Elements MUST NOT contain content that their content model disallows."
537    ##   ... Some are parse error, some are not (will be reported by c.c.).
538    ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO
539    ## - Text (in elements, attributes, and comments) SHOULD NOT contain
540    ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)
541    
542    ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot
543    ## be detected by the HTML5 parsing algorithm:
544    ## - Text,
545    
546  sub _get_next_token ($) {  sub _get_next_token ($) {
547    my $self = shift;    my $self = shift;
548    if (@{$self->{token}}) {    if (@{$self->{token}}) {
# Line 412  sub _get_next_token ($) { Line 550  sub _get_next_token ($) {
550    }    }
551    
552    A: {    A: {
553      if ($self->{state} eq 'data') {      if ($self->{state} == DATA_STATE) {
554        if ($self->{next_input_character} == 0x0026) { # &        if ($self->{next_char} == 0x0026) { # &
555          if ($self->{content_model_flag} eq 'PCDATA' or          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
556              $self->{content_model_flag} eq 'RCDATA') {              not $self->{escape}) {
557            $self->{state} = 'entity data';            !!!cp (1);
558              $self->{state} = ENTITY_DATA_STATE;
559            !!!next-input-character;            !!!next-input-character;
560            redo A;            redo A;
561          } else {          } else {
562              !!!cp (2);
563            #            #
564          }          }
565        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{next_char} == 0x002D) { # -
566          if ($self->{content_model_flag} ne 'PLAINTEXT') {          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
567            $self->{state} = 'tag open';            unless ($self->{escape}) {
568                if ($self->{prev_char}->[0] == 0x002D and # -
569                    $self->{prev_char}->[1] == 0x0021 and # !
570                    $self->{prev_char}->[2] == 0x003C) { # <
571                  !!!cp (3);
572                  $self->{escape} = 1;
573                } else {
574                  !!!cp (4);
575                }
576              } else {
577                !!!cp (5);
578              }
579            }
580            
581            #
582          } elsif ($self->{next_char} == 0x003C) { # <
583            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
584                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
585                 not $self->{escape})) {
586              !!!cp (6);
587              $self->{state} = TAG_OPEN_STATE;
588            !!!next-input-character;            !!!next-input-character;
589            redo A;            redo A;
590          } else {          } else {
591              !!!cp (7);
592            #            #
593          }          }
594        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == 0x003E) { # >
595          !!!emit ({type => 'end-of-file'});          if ($self->{escape} and
596                ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
597              if ($self->{prev_char}->[0] == 0x002D and # -
598                  $self->{prev_char}->[1] == 0x002D) { # -
599                !!!cp (8);
600                delete $self->{escape};
601              } else {
602                !!!cp (9);
603              }
604            } else {
605              !!!cp (10);
606            }
607            
608            #
609          } elsif ($self->{next_char} == -1) {
610            !!!cp (11);
611            !!!emit ({type => END_OF_FILE_TOKEN,
612                      line => $self->{line}, column => $self->{column}});
613          last A; ## TODO: ok?          last A; ## TODO: ok?
614          } else {
615            !!!cp (12);
616        }        }
617        # Anything else        # Anything else
618        my $token = {type => 'character',        my $token = {type => CHARACTER_TOKEN,
619                     data => chr $self->{next_input_character}};                     data => chr $self->{next_char},
620                       line => $self->{line}, column => $self->{column},
621                      };
622        ## Stay in the data state        ## Stay in the data state
623        !!!next-input-character;        !!!next-input-character;
624    
625        !!!emit ($token);        !!!emit ($token);
626    
627        redo A;        redo A;
628      } elsif ($self->{state} eq 'entity data') {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
629        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
630    
631          my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
632                
633        my $token = $self->_tokenize_attempt_to_consume_an_entity;        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);
634    
635        $self->{state} = 'data';        $self->{state} = DATA_STATE;
636        # next-input-character is already done        # next-input-character is already done
637    
638        unless (defined $token) {        unless (defined $token) {
639          !!!emit ({type => 'character', data => '&'});          !!!cp (13);
640            !!!emit ({type => CHARACTER_TOKEN, data => '&',
641                      line => $l, column => $c,
642                     });
643        } else {        } else {
644            !!!cp (14);
645          !!!emit ($token);          !!!emit ($token);
646        }        }
647    
648        redo A;        redo A;
649      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
650        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
651            $self->{content_model_flag} eq 'CDATA') {          if ($self->{next_char} == 0x002F) { # /
652          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
653            !!!next-input-character;            !!!next-input-character;
654            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
655            redo A;            redo A;
656          } else {          } else {
657              !!!cp (16);
658            ## reconsume            ## reconsume
659            $self->{state} = 'data';            $self->{state} = DATA_STATE;
660    
661            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
662                        line => $self->{line_prev},
663                        column => $self->{column_prev},
664                       });
665    
666            redo A;            redo A;
667          }          }
668        } elsif ($self->{content_model_flag} eq 'PCDATA') {        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
669          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{next_char} == 0x0021) { # !
670            $self->{state} = 'markup declaration open';            !!!cp (17);
671              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
672            !!!next-input-character;            !!!next-input-character;
673            redo A;            redo A;
674          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{next_char} == 0x002F) { # /
675            $self->{state} = 'close tag open';            !!!cp (18);
676              $self->{state} = CLOSE_TAG_OPEN_STATE;
677            !!!next-input-character;            !!!next-input-character;
678            redo A;            redo A;
679          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_char} and
680                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{next_char} <= 0x005A) { # A..Z
681              !!!cp (19);
682            $self->{current_token}            $self->{current_token}
683              = {type => 'start tag',              = {type => START_TAG_TOKEN,
684                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{next_char} + 0x0020),
685            $self->{state} = 'tag name';                 line => $self->{line_prev},
686                   column => $self->{column_prev}};
687              $self->{state} = TAG_NAME_STATE;
688            !!!next-input-character;            !!!next-input-character;
689            redo A;            redo A;
690          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_char} and
691                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{next_char} <= 0x007A) { # a..z
692            $self->{current_token} = {type => 'start tag',            !!!cp (20);
693                              tag_name => chr ($self->{next_input_character})};            $self->{current_token} = {type => START_TAG_TOKEN,
694            $self->{state} = 'tag name';                                      tag_name => chr ($self->{next_char}),
695                                        line => $self->{line_prev},
696                                        column => $self->{column_prev}};
697              $self->{state} = TAG_NAME_STATE;
698            !!!next-input-character;            !!!next-input-character;
699            redo A;            redo A;
700          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_char} == 0x003E) { # >
701            !!!parse-error (type => 'empty start tag');            !!!cp (21);
702            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
703                              line => $self->{line_prev},
704                              column => $self->{column_prev});
705              $self->{state} = DATA_STATE;
706            !!!next-input-character;            !!!next-input-character;
707    
708            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
709                        line => $self->{line_prev},
710                        column => $self->{column_prev},
711                       });
712    
713            redo A;            redo A;
714          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_char} == 0x003F) { # ?
715            !!!parse-error (type => 'pio');            !!!cp (22);
716            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
717            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
718                              column => $self->{column_prev});
719              $self->{state} = BOGUS_COMMENT_STATE;
720              $self->{current_token} = {type => COMMENT_TOKEN, data => '',
721                                        line => $self->{line_prev},
722                                        column => $self->{column_prev},
723                                       };
724              ## $self->{next_char} is intentionally left as is
725            redo A;            redo A;
726          } else {          } else {
727              !!!cp (23);
728            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago');
729            $self->{state} = 'data';            $self->{state} = DATA_STATE;
730            ## reconsume            ## reconsume
731    
732            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
733                        line => $self->{line_prev},
734                        column => $self->{column_prev},
735                       });
736    
737            redo A;            redo A;
738          }          }
739        } else {        } else {
740          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
741        }        }
742      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
743        if ($self->{content_model_flag} eq 'RCDATA' or        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
744            $self->{content_model_flag} eq 'CDATA') {        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
745          my @next_char;          if (defined $self->{last_emitted_start_tag_name}) {
746          TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
747            push @next_char, $self->{next_input_character};            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
748            my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);            my @next_char;
749            my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {
750            if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {              push @next_char, $self->{next_char};
751              !!!next-input-character;              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);
752              next TAGNAME;              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;
753            } else {              if ($self->{next_char} == $c or $self->{next_char} == $C) {
754              !!!parse-error (type => 'unmatched end tag');                !!!cp (24);
755              $self->{next_input_character} = shift @next_char; # reconsume                !!!next-input-character;
756                  next TAGNAME;
757                } else {
758                  !!!cp (25);
759                  $self->{next_char} = shift @next_char; # reconsume
760                  !!!back-next-input-character (@next_char);
761                  $self->{state} = DATA_STATE;
762    
763                  !!!emit ({type => CHARACTER_TOKEN, data => '</',
764                            line => $l, column => $c,
765                           });
766      
767                  redo A;
768                }
769              }
770              push @next_char, $self->{next_char};
771          
772              unless ($self->{next_char} == 0x0009 or # HT
773                      $self->{next_char} == 0x000A or # LF
774                      $self->{next_char} == 0x000B or # VT
775                      $self->{next_char} == 0x000C or # FF
776                      $self->{next_char} == 0x0020 or # SP
777                      $self->{next_char} == 0x003E or # >
778                      $self->{next_char} == 0x002F or # /
779                      $self->{next_char} == -1) {
780                !!!cp (26);
781                $self->{next_char} = shift @next_char; # reconsume
782              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
783              $self->{state} = 'data';              $self->{state} = DATA_STATE;
784                !!!emit ({type => CHARACTER_TOKEN, data => '</',
785              !!!emit ({type => 'character', data => '</'});                        line => $l, column => $c,
786                         });
787              redo A;              redo A;
788              } else {
789                !!!cp (27);
790                $self->{next_char} = shift @next_char;
791                !!!back-next-input-character (@next_char);
792                # and consume...
793            }            }
         }  
         push @next_char, $self->{next_input_character};  
       
         unless ($self->{next_input_character} == 0x0009 or # HT  
                 $self->{next_input_character} == 0x000A or # LF  
                 $self->{next_input_character} == 0x000B or # VT  
                 $self->{next_input_character} == 0x000C or # FF  
                 $self->{next_input_character} == 0x0020 or # SP  
                 $self->{next_input_character} == 0x003E or # >  
                 $self->{next_input_character} == 0x002F or # /  
                 $self->{next_input_character} == 0x003C or # <  
                 $self->{next_input_character} == -1) {  
           !!!parse-error (type => 'unmatched end tag');  
           $self->{next_input_character} = shift @next_char; # reconsume  
           !!!back-next-input-character (@next_char);  
           $self->{state} = 'data';  
   
           !!!emit ({type => 'character', data => '</'});  
   
           redo A;  
794          } else {          } else {
795            $self->{next_input_character} = shift @next_char;            ## No start tag token has ever been emitted
796            !!!back-next-input-character (@next_char);            !!!cp (28);
797            # and consume...            # next-input-character is already done
798              $self->{state} = DATA_STATE;
799              !!!emit ({type => CHARACTER_TOKEN, data => '</',
800                        line => $l, column => $c,
801                       });
802              redo A;
803          }          }
804        }        }
805                
806        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{next_char} and
807            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{next_char} <= 0x005A) { # A..Z
808          $self->{current_token} = {type => 'end tag',          !!!cp (29);
809                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{current_token}
810          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
811          !!!next-input-character;                 tag_name => chr ($self->{next_char} + 0x0020),
812          redo A;                 line => $l, column => $c};
813        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
814                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
815          $self->{current_token} = {type => 'end tag',          redo A;
816                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{next_char} and
817          $self->{state} = 'tag name';                 $self->{next_char} <= 0x007A) { # a..z
818          !!!next-input-character;          !!!cp (30);
819          redo A;          $self->{current_token} = {type => END_TAG_TOKEN,
820        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{next_char}),
821          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
822          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
823            !!!next-input-character;
824            redo A;
825          } elsif ($self->{next_char} == 0x003E) { # >
826            !!!cp (31);
827            !!!parse-error (type => 'empty end tag',
828                            line => $self->{line_prev}, ## "<" in "</>"
829                            column => $self->{column_prev} - 1);
830            $self->{state} = DATA_STATE;
831          !!!next-input-character;          !!!next-input-character;
832          redo A;          redo A;
833        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
834            !!!cp (32);
835          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
836          $self->{state} = 'data';          $self->{state} = DATA_STATE;
837          # reconsume          # reconsume
838    
839          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
840                      line => $l, column => $c,
841                     });
842    
843          redo A;          redo A;
844        } else {        } else {
845            !!!cp (33);
846          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
847          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
848          ## $self->{next_input_character} is intentionally left as is          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
849          redo A;                                    line => $self->{line_prev}, # "<" of "</"
850        }                                    column => $self->{column_prev} - 1,
851      } elsif ($self->{state} eq 'tag name') {                                   };
852        if ($self->{next_input_character} == 0x0009 or # HT          ## $self->{next_char} is intentionally left as is
853            $self->{next_input_character} == 0x000A or # LF          redo A;
854            $self->{next_input_character} == 0x000B or # VT        }
855            $self->{next_input_character} == 0x000C or # FF      } elsif ($self->{state} == TAG_NAME_STATE) {
856            $self->{next_input_character} == 0x0020) { # SP        if ($self->{next_char} == 0x0009 or # HT
857          $self->{state} = 'before attribute name';            $self->{next_char} == 0x000A or # LF
858          !!!next-input-character;            $self->{next_char} == 0x000B or # VT
859          redo A;            $self->{next_char} == 0x000C or # FF
860        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{next_char} == 0x0020) { # SP
861          if ($self->{current_token}->{type} eq 'start tag') {          !!!cp (34);
862            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
863            !!!next-input-character;
864            redo A;
865          } elsif ($self->{next_char} == 0x003E) { # >
866            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
867              !!!cp (35);
868            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
869          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
870            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
871            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
872              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
873            }            #  !!! cp (36);
874              #  !!! parse-error (type => 'end tag attribute');
875              #} else {
876                !!!cp (37);
877              #}
878          } else {          } else {
879            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
880          }          }
881          $self->{state} = 'data';          $self->{state} = DATA_STATE;
882          !!!next-input-character;          !!!next-input-character;
883    
884          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
885    
886          redo A;          redo A;
887        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
888                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
889          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
890            $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);
891            # start tag or end tag            # start tag or end tag
892          ## Stay in this state          ## Stay in this state
893          !!!next-input-character;          !!!next-input-character;
894          redo A;          redo A;
895        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
896          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
897          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
898              !!!cp (39);
899            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
900          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
901            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
902            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
903              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
904            }            #  !!! cp (40);
905              #  !!! parse-error (type => 'end tag attribute');
906              #} else {
907                !!!cp (41);
908              #}
909          } else {          } else {
910            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
911          }          }
912          $self->{state} = 'data';          $self->{state} = DATA_STATE;
913          # reconsume          # reconsume
914    
915          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
916    
917          redo A;          redo A;
918        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
919          !!!next-input-character;          !!!next-input-character;
920          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_char} == 0x003E and # >
921              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
922              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
923            # permitted slash            # permitted slash
924              !!!cp (42);
925            #            #
926          } else {          } else {
927              !!!cp (43);
928            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
929          }          }
930          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
931          # next-input-character is already done          # next-input-character is already done
932          redo A;          redo A;
933        } else {        } else {
934          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
935            $self->{current_token}->{tag_name} .= chr $self->{next_char};
936            # start tag or end tag            # start tag or end tag
937          ## Stay in the state          ## Stay in the state
938          !!!next-input-character;          !!!next-input-character;
939          redo A;          redo A;
940        }        }
941      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
942        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
943            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
944            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
945            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
946            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
947            !!!cp (45);
948          ## Stay in the state          ## Stay in the state
949          !!!next-input-character;          !!!next-input-character;
950          redo A;          redo A;
951        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
952          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
953              !!!cp (46);
954            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
955          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
956            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
957            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
958                !!!cp (47);
959              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
960              } else {
961                !!!cp (48);
962            }            }
963          } else {          } else {
964            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
965          }          }
966          $self->{state} = 'data';          $self->{state} = DATA_STATE;
967          !!!next-input-character;          !!!next-input-character;
968    
969          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
970    
971          redo A;          redo A;
972        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
973                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
974          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
975                                value => ''};          $self->{current_attribute}
976          $self->{state} = 'attribute name';              = {name => chr ($self->{next_char} + 0x0020),
977                   value => '',
978                   line => $self->{line}, column => $self->{column}};
979            $self->{state} = ATTRIBUTE_NAME_STATE;
980          !!!next-input-character;          !!!next-input-character;
981          redo A;          redo A;
982        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
983          !!!next-input-character;          !!!next-input-character;
984          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_char} == 0x003E and # >
985              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
986              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
987            # permitted slash            # permitted slash
988              !!!cp (50);
989            #            #
990          } else {          } else {
991              !!!cp (51);
992            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
993          }          }
994          ## Stay in the state          ## Stay in the state
995          # next-input-character is already done          # next-input-character is already done
996          redo A;          redo A;
997        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
998          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
999          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1000              !!!cp (52);
1001            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1002          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1003            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1004            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1005                !!!cp (53);
1006              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1007              } else {
1008                !!!cp (54);
1009            }            }
1010          } else {          } else {
1011            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1012          }          }
1013          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1014          # reconsume          # reconsume
1015    
1016          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1017    
1018          redo A;          redo A;
1019        } else {        } else {
1020          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1021                                value => ''};               0x0022 => 1, # "
1022          $self->{state} = 'attribute name';               0x0027 => 1, # '
1023                 0x003D => 1, # =
1024                }->{$self->{next_char}}) {
1025              !!!cp (55);
1026              !!!parse-error (type => 'bad attribute name');
1027            } else {
1028              !!!cp (56);
1029            }
1030            $self->{current_attribute}
1031                = {name => chr ($self->{next_char}),
1032                   value => '',
1033                   line => $self->{line}, column => $self->{column}};
1034            $self->{state} = ATTRIBUTE_NAME_STATE;
1035          !!!next-input-character;          !!!next-input-character;
1036          redo A;          redo A;
1037        }        }
1038      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1039        my $before_leave = sub {        my $before_leave = sub {
1040          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
1041              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
1042            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1043              !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
1044            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
1045          } else {          } else {
1046              !!!cp (58);
1047            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
1048              = $self->{current_attribute};              = $self->{current_attribute};
1049          }          }
1050        }; # $before_leave        }; # $before_leave
1051    
1052        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1053            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1054            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1055            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1056            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1057            !!!cp (59);
1058          $before_leave->();          $before_leave->();
1059          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1060          !!!next-input-character;          !!!next-input-character;
1061          redo A;          redo A;
1062        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1063            !!!cp (60);
1064          $before_leave->();          $before_leave->();
1065          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1066          !!!next-input-character;          !!!next-input-character;
1067          redo A;          redo A;
1068        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1069          $before_leave->();          $before_leave->();
1070          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1071              !!!cp (61);
1072            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1073          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1074            $self->{content_model_flag} = 'PCDATA'; # MUST            !!!cp (62);
1075              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1076            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1077              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1078            }            }
1079          } else {          } else {
1080            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1081          }          }
1082          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1083          !!!next-input-character;          !!!next-input-character;
1084    
1085          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1086    
1087          redo A;          redo A;
1088        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1089                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1090          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1091            $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);
1092          ## Stay in the state          ## Stay in the state
1093          !!!next-input-character;          !!!next-input-character;
1094          redo A;          redo A;
1095        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1096          $before_leave->();          $before_leave->();
1097          !!!next-input-character;          !!!next-input-character;
1098          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_char} == 0x003E and # >
1099              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
1100              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1101            # permitted slash            # permitted slash
1102              !!!cp (64);
1103            #            #
1104          } else {          } else {
1105              !!!cp (65);
1106            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
1107          }          }
1108          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1109          # next-input-character is already done          # next-input-character is already done
1110          redo A;          redo A;
1111        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
1112          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1113          $before_leave->();          $before_leave->();
1114          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1115              !!!cp (66);
1116            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1117          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1118            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1119            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1120                !!!cp (67);
1121              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1122              } else {
1123                ## NOTE: This state should never be reached.
1124                !!!cp (68);
1125            }            }
1126          } else {          } else {
1127            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1128          }          }
1129          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1130          # reconsume          # reconsume
1131    
1132          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1133    
1134          redo A;          redo A;
1135        } else {        } else {
1136          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{next_char} == 0x0022 or # "
1137                $self->{next_char} == 0x0027) { # '
1138              !!!cp (69);
1139              !!!parse-error (type => 'bad attribute name');
1140            } else {
1141              !!!cp (70);
1142            }
1143            $self->{current_attribute}->{name} .= chr ($self->{next_char});
1144          ## Stay in the state          ## Stay in the state
1145          !!!next-input-character;          !!!next-input-character;
1146          redo A;          redo A;
1147        }        }
1148      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1149        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1150            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1151            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1152            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1153            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1154            !!!cp (71);
1155          ## Stay in the state          ## Stay in the state
1156          !!!next-input-character;          !!!next-input-character;
1157          redo A;          redo A;
1158        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1159          $self->{state} = 'before attribute value';          !!!cp (72);
1160            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1161          !!!next-input-character;          !!!next-input-character;
1162          redo A;          redo A;
1163        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1164          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1165              !!!cp (73);
1166            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1167          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1168            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1169            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1170                !!!cp (74);
1171              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1172              } else {
1173                ## NOTE: This state should never be reached.
1174                !!!cp (75);
1175            }            }
1176          } else {          } else {
1177            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1178          }          }
1179          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1180          !!!next-input-character;          !!!next-input-character;
1181    
1182          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1183    
1184          redo A;          redo A;
1185        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1186                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1187          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1188                                value => ''};          $self->{current_attribute}
1189          $self->{state} = 'attribute name';              = {name => chr ($self->{next_char} + 0x0020),
1190                   value => '',
1191                   line => $self->{line}, column => $self->{column}};
1192            $self->{state} = ATTRIBUTE_NAME_STATE;
1193          !!!next-input-character;          !!!next-input-character;
1194          redo A;          redo A;
1195        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1196          !!!next-input-character;          !!!next-input-character;
1197          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_char} == 0x003E and # >
1198              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
1199              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1200            # permitted slash            # permitted slash
1201              !!!cp (77);
1202            #            #
1203          } else {          } else {
1204              !!!cp (78);
1205            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
1206              ## TODO: Different error type for <aa / bb> than <aa/>
1207          }          }
1208          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1209          # next-input-character is already done          # next-input-character is already done
1210          redo A;          redo A;
1211        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
1212          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1213          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1214              !!!cp (79);
1215            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1216          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1217            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1218            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1219                !!!cp (80);
1220              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1221              } else {
1222                ## NOTE: This state should never be reached.
1223                !!!cp (81);
1224            }            }
1225          } else {          } else {
1226            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1227          }          }
1228          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1229          # reconsume          # reconsume
1230    
1231          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1232    
1233          redo A;          redo A;
1234        } else {        } else {
1235          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          !!!cp (82);
1236                                value => ''};          $self->{current_attribute}
1237          $self->{state} = 'attribute name';              = {name => chr ($self->{next_char}),
1238                   value => '',
1239                   line => $self->{line}, column => $self->{column}};
1240            $self->{state} = ATTRIBUTE_NAME_STATE;
1241          !!!next-input-character;          !!!next-input-character;
1242          redo A;                  redo A;        
1243        }        }
1244      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1245        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1246            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1247            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1248            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1249            $self->{next_input_character} == 0x0020) { # SP                  $self->{next_char} == 0x0020) { # SP      
1250            !!!cp (83);
1251          ## Stay in the state          ## Stay in the state
1252          !!!next-input-character;          !!!next-input-character;
1253          redo A;          redo A;
1254        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
1255          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1256            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1257          !!!next-input-character;          !!!next-input-character;
1258          redo A;          redo A;
1259        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1260          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1261            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1262          ## reconsume          ## reconsume
1263          redo A;          redo A;
1264        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
1265          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1266            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1267          !!!next-input-character;          !!!next-input-character;
1268          redo A;          redo A;
1269        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1270          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1271              !!!cp (87);
1272            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1273          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1274            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1275            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1276                !!!cp (88);
1277              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1278              } else {
1279                ## NOTE: This state should never be reached.
1280                !!!cp (89);
1281            }            }
1282          } else {          } else {
1283            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1284          }          }
1285          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1286          !!!next-input-character;          !!!next-input-character;
1287    
1288          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1289    
1290          redo A;          redo A;
1291        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
1292          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1293          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1294              !!!cp (90);
1295            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1296          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1297            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1298            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1299                !!!cp (91);
1300              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1301              } else {
1302                ## NOTE: This state should never be reached.
1303                !!!cp (92);
1304            }            }
1305          } else {          } else {
1306            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1307          }          }
1308          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1309          ## reconsume          ## reconsume
1310    
1311          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1312    
1313          redo A;          redo A;
1314        } else {        } else {
1315          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{next_char} == 0x003D) { # =
1316          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1317              !!!parse-error (type => 'bad attribute value');
1318            } else {
1319              !!!cp (94);
1320            }
1321            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1322            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1323          !!!next-input-character;          !!!next-input-character;
1324          redo A;          redo A;
1325        }        }
1326      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1327        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
1328          $self->{state} = 'before attribute name';          !!!cp (95);
1329            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1330          !!!next-input-character;          !!!next-input-character;
1331          redo A;          redo A;
1332        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1333          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1334          $self->{state} = 'entity in attribute value';          $self->{last_attribute_value_state} = $self->{state};
1335            $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1336          !!!next-input-character;          !!!next-input-character;
1337          redo A;          redo A;
1338        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1339          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1340          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1341              !!!cp (97);
1342            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1343          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1344            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1345            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1346                !!!cp (98);
1347              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1348              } else {
1349                ## NOTE: This state should never be reached.
1350                !!!cp (99);
1351            }            }
1352          } else {          } else {
1353            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1354          }          }
1355          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1356          ## reconsume          ## reconsume
1357    
1358          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1359    
1360          redo A;          redo A;
1361        } else {        } else {
1362          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1363            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1364          ## Stay in the state          ## Stay in the state
1365          !!!next-input-character;          !!!next-input-character;
1366          redo A;          redo A;
1367        }        }
1368      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1369        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
1370          $self->{state} = 'before attribute name';          !!!cp (101);
1371            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1372          !!!next-input-character;          !!!next-input-character;
1373          redo A;          redo A;
1374        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1375          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';          !!!cp (102);
1376          $self->{state} = 'entity in attribute value';          $self->{last_attribute_value_state} = $self->{state};
1377            $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1378          !!!next-input-character;          !!!next-input-character;
1379          redo A;          redo A;
1380        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1381          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1382          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1383              !!!cp (103);
1384            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1385          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1386            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1387            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1388                !!!cp (104);
1389              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1390              } else {
1391                ## NOTE: This state should never be reached.
1392                !!!cp (105);
1393            }            }
1394          } else {          } else {
1395            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1396          }          }
1397          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1398          ## reconsume          ## reconsume
1399    
1400          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1401    
1402          redo A;          redo A;
1403        } else {        } else {
1404          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1405            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1406          ## Stay in the state          ## Stay in the state
1407          !!!next-input-character;          !!!next-input-character;
1408          redo A;          redo A;
1409        }        }
1410      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1411        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1412            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1413            $self->{next_input_character} == 0x000B or # HT            $self->{next_char} == 0x000B or # HT
1414            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1415            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1416          $self->{state} = 'before attribute name';          !!!cp (107);
1417          !!!next-input-character;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1418          redo A;          !!!next-input-character;
1419        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1420          $self->{last_attribute_value_state} = 'attribute value (unquoted)';        } elsif ($self->{next_char} == 0x0026) { # &
1421          $self->{state} = 'entity in attribute value';          !!!cp (108);
1422          !!!next-input-character;          $self->{last_attribute_value_state} = $self->{state};
1423          redo A;          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1424        } elsif ($self->{next_input_character} == 0x003E) { # >          !!!next-input-character;
1425          if ($self->{current_token}->{type} eq 'start tag') {          redo A;
1426          } elsif ($self->{next_char} == 0x003E) { # >
1427            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1428              !!!cp (109);
1429            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1430          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1431            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1432            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1433                !!!cp (110);
1434              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1435              } else {
1436                ## NOTE: This state should never be reached.
1437                !!!cp (111);
1438            }            }
1439          } else {          } else {
1440            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1441          }          }
1442          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1443          !!!next-input-character;          !!!next-input-character;
1444    
1445          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1446    
1447          redo A;          redo A;
1448        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_char} == -1) {
                $self->{next_input_character} == -1) {  
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1451              !!!cp (112);
1452            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1453          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1454            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1456                !!!cp (113);
1457              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1458              } else {
1459                ## NOTE: This state should never be reached.
1460                !!!cp (114);
1461            }            }
1462          } else {          } else {
1463            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1464          }          }
1465          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1466          ## reconsume          ## reconsume
1467    
1468          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
         undef $self->{current_token};  
1469    
1470          redo A;          redo A;
1471        } else {        } else {
1472          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1473                 0x0022 => 1, # "
1474                 0x0027 => 1, # '
1475                 0x003D => 1, # =
1476                }->{$self->{next_char}}) {
1477              !!!cp (115);
1478              !!!parse-error (type => 'bad attribute value');
1479            } else {
1480              !!!cp (116);
1481            }
1482            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1483          ## Stay in the state          ## Stay in the state
1484          !!!next-input-character;          !!!next-input-character;
1485          redo A;          redo A;
1486        }        }
1487      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {
1488        my $token = $self->_tokenize_attempt_to_consume_an_entity;        my $token = $self->_tokenize_attempt_to_consume_an_entity
1489              (1,
1490               $self->{last_attribute_value_state}
1491                 == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "
1492               $self->{last_attribute_value_state}
1493                 == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '
1494               -1);
1495    
1496        unless (defined $token) {        unless (defined $token) {
1497            !!!cp (117);
1498          $self->{current_attribute}->{value} .= '&';          $self->{current_attribute}->{value} .= '&';
1499        } else {        } else {
1500            !!!cp (118);
1501          $self->{current_attribute}->{value} .= $token->{data};          $self->{current_attribute}->{value} .= $token->{data};
1502            $self->{current_attribute}->{has_reference} = $token->{has_reference};
1503          ## ISSUE: spec says "append the returned character token to the current attribute's value"          ## ISSUE: spec says "append the returned character token to the current attribute's value"
1504        }        }
1505    
1506        $self->{state} = $self->{last_attribute_value_state};        $self->{state} = $self->{last_attribute_value_state};
1507        # next-input-character is already done        # next-input-character is already done
1508        redo A;        redo A;
1509      } elsif ($self->{state} eq 'bogus comment') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1510          if ($self->{next_char} == 0x0009 or # HT
1511              $self->{next_char} == 0x000A or # LF
1512              $self->{next_char} == 0x000B or # VT
1513              $self->{next_char} == 0x000C or # FF
1514              $self->{next_char} == 0x0020) { # SP
1515            !!!cp (118);
1516            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1517            !!!next-input-character;
1518            redo A;
1519          } elsif ($self->{next_char} == 0x003E) { # >
1520            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1521              !!!cp (119);
1522              $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1523            } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1524              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525              if ($self->{current_token}->{attributes}) {
1526                !!!cp (120);
1527                !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                ## NOTE: This state should never be reached.
1530                !!!cp (121);
1531              }
1532            } else {
1533              die "$0: $self->{current_token}->{type}: Unknown token type";
1534            }
1535            $self->{state} = DATA_STATE;
1536            !!!next-input-character;
1537    
1538            !!!emit ($self->{current_token}); # start tag or end tag
1539    
1540            redo A;
1541          } elsif ($self->{next_char} == 0x002F) { # /
1542            !!!next-input-character;
1543            if ($self->{next_char} == 0x003E and # >
1544                $self->{current_token}->{type} == START_TAG_TOKEN and
1545                $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1546              # permitted slash
1547              !!!cp (122);
1548              #
1549            } else {
1550              !!!cp (123);
1551              !!!parse-error (type => 'nestc');
1552            }
1553            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1554            # next-input-character is already done
1555            redo A;
1556          } else {
1557            !!!cp (124);
1558            !!!parse-error (type => 'no space between attributes');
1559            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1560            ## reconsume
1561            redo A;
1562          }
1563        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1564        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1565                
1566        my $token = {type => 'comment', data => ''};        ## NOTE: Set by the previous state
1567          #my $token = {type => COMMENT_TOKEN, data => ''};
1568    
1569        BC: {        BC: {
1570          if ($self->{next_input_character} == 0x003E) { # >          if ($self->{next_char} == 0x003E) { # >
1571            $self->{state} = 'data';            !!!cp (124);
1572              $self->{state} = DATA_STATE;
1573            !!!next-input-character;            !!!next-input-character;
1574    
1575            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1576    
1577            redo A;            redo A;
1578          } elsif ($self->{next_input_character} == -1) {          } elsif ($self->{next_char} == -1) {
1579            $self->{state} = 'data';            !!!cp (125);
1580              $self->{state} = DATA_STATE;
1581            ## reconsume            ## reconsume
1582    
1583            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1584    
1585            redo A;            redo A;
1586          } else {          } else {
1587            $token->{data} .= chr ($self->{next_input_character});            !!!cp (126);
1588              $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1589            !!!next-input-character;            !!!next-input-character;
1590            redo BC;            redo BC;
1591          }          }
1592        } # BC        } # BC
1593      } elsif ($self->{state} eq 'markup declaration open') {  
1594          die "$0: _get_next_token: unexpected case [BC]";
1595        } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1596        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1597    
1598          my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);
1599    
1600        my @next_char;        my @next_char;
1601        push @next_char, $self->{next_input_character};        push @next_char, $self->{next_char};
1602                
1603        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1604          !!!next-input-character;          !!!next-input-character;
1605          push @next_char, $self->{next_input_character};          push @next_char, $self->{next_char};
1606          if ($self->{next_input_character} == 0x002D) { # -          if ($self->{next_char} == 0x002D) { # -
1607            $self->{current_token} = {type => 'comment', data => ''};            !!!cp (127);
1608            $self->{state} = 'comment';            $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1609                                        line => $l, column => $c,
1610                                       };
1611              $self->{state} = COMMENT_START_STATE;
1612            !!!next-input-character;            !!!next-input-character;
1613            redo A;            redo A;
1614            } else {
1615              !!!cp (128);
1616          }          }
1617        } elsif ($self->{next_input_character} == 0x0044 or # D        } elsif ($self->{next_char} == 0x0044 or # D
1618                 $self->{next_input_character} == 0x0064) { # d                 $self->{next_char} == 0x0064) { # d
1619          !!!next-input-character;          !!!next-input-character;
1620          push @next_char, $self->{next_input_character};          push @next_char, $self->{next_char};
1621          if ($self->{next_input_character} == 0x004F or # O          if ($self->{next_char} == 0x004F or # O
1622              $self->{next_input_character} == 0x006F) { # o              $self->{next_char} == 0x006F) { # o
1623            !!!next-input-character;            !!!next-input-character;
1624            push @next_char, $self->{next_input_character};            push @next_char, $self->{next_char};
1625            if ($self->{next_input_character} == 0x0043 or # C            if ($self->{next_char} == 0x0043 or # C
1626                $self->{next_input_character} == 0x0063) { # c                $self->{next_char} == 0x0063) { # c
1627              !!!next-input-character;              !!!next-input-character;
1628              push @next_char, $self->{next_input_character};              push @next_char, $self->{next_char};
1629              if ($self->{next_input_character} == 0x0054 or # T              if ($self->{next_char} == 0x0054 or # T
1630                  $self->{next_input_character} == 0x0074) { # t                  $self->{next_char} == 0x0074) { # t
1631                !!!next-input-character;                !!!next-input-character;
1632                push @next_char, $self->{next_input_character};                push @next_char, $self->{next_char};
1633                if ($self->{next_input_character} == 0x0059 or # Y                if ($self->{next_char} == 0x0059 or # Y
1634                    $self->{next_input_character} == 0x0079) { # y                    $self->{next_char} == 0x0079) { # y
1635                  !!!next-input-character;                  !!!next-input-character;
1636                  push @next_char, $self->{next_input_character};                  push @next_char, $self->{next_char};
1637                  if ($self->{next_input_character} == 0x0050 or # P                  if ($self->{next_char} == 0x0050 or # P
1638                      $self->{next_input_character} == 0x0070) { # p                      $self->{next_char} == 0x0070) { # p
1639                    !!!next-input-character;                    !!!next-input-character;
1640                    push @next_char, $self->{next_input_character};                    push @next_char, $self->{next_char};
1641                    if ($self->{next_input_character} == 0x0045 or # E                    if ($self->{next_char} == 0x0045 or # E
1642                        $self->{next_input_character} == 0x0065) { # e                        $self->{next_char} == 0x0065) { # e
1643                      ## ISSUE: What a stupid code this is!                      !!!cp (129);
1644                      $self->{state} = 'DOCTYPE';                      ## TODO: What a stupid code this is!
1645                        $self->{state} = DOCTYPE_STATE;
1646                        $self->{current_token} = {type => DOCTYPE_TOKEN,
1647                                                  quirks => 1,
1648                                                  line => $l, column => $c,
1649                                                 };
1650                      !!!next-input-character;                      !!!next-input-character;
1651                      redo A;                      redo A;
1652                      } else {
1653                        !!!cp (130);
1654                    }                    }
1655                    } else {
1656                      !!!cp (131);
1657                  }                  }
1658                  } else {
1659                    !!!cp (132);
1660                }                }
1661                } else {
1662                  !!!cp (133);
1663              }              }
1664              } else {
1665                !!!cp (134);
1666            }            }
1667            } else {
1668              !!!cp (135);
1669          }          }
1670          } else {
1671            !!!cp (136);
1672        }        }
1673    
1674        !!!parse-error (type => 'bogus comment open');        !!!parse-error (type => 'bogus comment');
1675        $self->{next_input_character} = shift @next_char;        $self->{next_char} = shift @next_char;
1676        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1677        $self->{state} = 'bogus comment';        $self->{state} = BOGUS_COMMENT_STATE;
1678          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1679                                    line => $l, column => $c,
1680                                   };
1681        redo A;        redo A;
1682                
1683        ## ISSUE: typos in spec: chacacters, is is a parse error        ## ISSUE: typos in spec: chacacters, is is a parse error
1684        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?
1685      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_START_STATE) {
1686        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1687          $self->{state} = 'comment dash';          !!!cp (137);
1688            $self->{state} = COMMENT_START_DASH_STATE;
1689            !!!next-input-character;
1690            redo A;
1691          } elsif ($self->{next_char} == 0x003E) { # >
1692            !!!cp (138);
1693            !!!parse-error (type => 'bogus comment');
1694            $self->{state} = DATA_STATE;
1695            !!!next-input-character;
1696    
1697            !!!emit ($self->{current_token}); # comment
1698    
1699            redo A;
1700          } elsif ($self->{next_char} == -1) {
1701            !!!cp (139);
1702            !!!parse-error (type => 'unclosed comment');
1703            $self->{state} = DATA_STATE;
1704            ## reconsume
1705    
1706            !!!emit ($self->{current_token}); # comment
1707    
1708            redo A;
1709          } else {
1710            !!!cp (140);
1711            $self->{current_token}->{data} # comment
1712                .= chr ($self->{next_char});
1713            $self->{state} = COMMENT_STATE;
1714          !!!next-input-character;          !!!next-input-character;
1715          redo A;          redo A;
1716        } elsif ($self->{next_input_character} == -1) {        }
1717        } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
1718          if ($self->{next_char} == 0x002D) { # -
1719            !!!cp (141);
1720            $self->{state} = COMMENT_END_STATE;
1721            !!!next-input-character;
1722            redo A;
1723          } elsif ($self->{next_char} == 0x003E) { # >
1724            !!!cp (142);
1725            !!!parse-error (type => 'bogus comment');
1726            $self->{state} = DATA_STATE;
1727            !!!next-input-character;
1728    
1729            !!!emit ($self->{current_token}); # comment
1730    
1731            redo A;
1732          } elsif ($self->{next_char} == -1) {
1733            !!!cp (143);
1734          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1735          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1736          ## reconsume          ## reconsume
1737    
1738          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
         undef $self->{current_token};  
1739    
1740          redo A;          redo A;
1741        } else {        } else {
1742          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (144);
1743            $self->{current_token}->{data} # comment
1744                .= '-' . chr ($self->{next_char});
1745            $self->{state} = COMMENT_STATE;
1746            !!!next-input-character;
1747            redo A;
1748          }
1749        } elsif ($self->{state} == COMMENT_STATE) {
1750          if ($self->{next_char} == 0x002D) { # -
1751            !!!cp (145);
1752            $self->{state} = COMMENT_END_DASH_STATE;
1753            !!!next-input-character;
1754            redo A;
1755          } elsif ($self->{next_char} == -1) {
1756            !!!cp (146);
1757            !!!parse-error (type => 'unclosed comment');
1758            $self->{state} = DATA_STATE;
1759            ## reconsume
1760    
1761            !!!emit ($self->{current_token}); # comment
1762    
1763            redo A;
1764          } else {
1765            !!!cp (147);
1766            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1767          ## Stay in the state          ## Stay in the state
1768          !!!next-input-character;          !!!next-input-character;
1769          redo A;          redo A;
1770        }        }
1771      } elsif ($self->{state} eq 'comment dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
1772        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1773          $self->{state} = 'comment end';          !!!cp (148);
1774            $self->{state} = COMMENT_END_STATE;
1775          !!!next-input-character;          !!!next-input-character;
1776          redo A;          redo A;
1777        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1778            !!!cp (149);
1779          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1780          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1781          ## reconsume          ## reconsume
1782    
1783          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
         undef $self->{current_token};  
1784    
1785          redo A;          redo A;
1786        } else {        } else {
1787          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
1788          $self->{state} = 'comment';          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
1789            $self->{state} = COMMENT_STATE;
1790          !!!next-input-character;          !!!next-input-character;
1791          redo A;          redo A;
1792        }        }
1793      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
1794        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
1795          $self->{state} = 'data';          !!!cp (151);
1796            $self->{state} = DATA_STATE;
1797          !!!next-input-character;          !!!next-input-character;
1798    
1799          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
         undef $self->{current_token};  
1800    
1801          redo A;          redo A;
1802        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
1803          !!!parse-error (type => 'dash in comment');          !!!cp (152);
1804            !!!parse-error (type => 'dash in comment',
1805                            line => $self->{line_prev},
1806                            column => $self->{column_prev});
1807          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
1808          ## Stay in the state          ## Stay in the state
1809          !!!next-input-character;          !!!next-input-character;
1810          redo A;          redo A;
1811        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1812            !!!cp (153);
1813          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1814          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1815          ## reconsume          ## reconsume
1816    
1817          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
         undef $self->{current_token};  
1818    
1819          redo A;          redo A;
1820        } else {        } else {
1821          !!!parse-error (type => 'dash in comment');          !!!cp (154);
1822          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
1823          $self->{state} = 'comment';                          line => $self->{line_prev},
1824                            column => $self->{column_prev});
1825            $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
1826            $self->{state} = COMMENT_STATE;
1827          !!!next-input-character;          !!!next-input-character;
1828          redo A;          redo A;
1829        }        }
1830      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
1831        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1832            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1833            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1834            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1835            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1836          $self->{state} = 'before DOCTYPE name';          !!!cp (155);
1837            $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1838          !!!next-input-character;          !!!next-input-character;
1839          redo A;          redo A;
1840        } else {        } else {
1841            !!!cp (156);
1842          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
1843          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1844          ## reconsume          ## reconsume
1845          redo A;          redo A;
1846        }        }
1847      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
1848        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1849            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1850            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1851            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1852            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1853            !!!cp (157);
1854          ## Stay in the state          ## Stay in the state
1855          !!!next-input-character;          !!!next-input-character;
1856          redo A;          redo A;
1857        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{next_char} == 0x003E) { # >
1858                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (158);
1859          $self->{current_token} = {type => 'DOCTYPE',          !!!parse-error (type => 'no DOCTYPE name');
1860                            name => chr ($self->{next_input_character} - 0x0020),          $self->{state} = DATA_STATE;
                           error => 1};  
         $self->{state} = 'DOCTYPE name';  
1861          !!!next-input-character;          !!!next-input-character;
1862    
1863            !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1864    
1865          redo A;          redo A;
1866        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == -1) {
1867            !!!cp (159);
1868          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1869          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1870            ## reconsume
1871    
1872            !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1873    
1874            redo A;
1875          } else {
1876            !!!cp (160);
1877            $self->{current_token}->{name} = chr $self->{next_char};
1878            delete $self->{current_token}->{quirks};
1879    ## ISSUE: "Set the token's name name to the" in the spec
1880            $self->{state} = DOCTYPE_NAME_STATE;
1881            !!!next-input-character;
1882            redo A;
1883          }
1884        } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
1885    ## ISSUE: Redundant "First," in the spec.
1886          if ($self->{next_char} == 0x0009 or # HT
1887              $self->{next_char} == 0x000A or # LF
1888              $self->{next_char} == 0x000B or # VT
1889              $self->{next_char} == 0x000C or # FF
1890              $self->{next_char} == 0x0020) { # SP
1891            !!!cp (161);
1892            $self->{state} = AFTER_DOCTYPE_NAME_STATE;
1893            !!!next-input-character;
1894            redo A;
1895          } elsif ($self->{next_char} == 0x003E) { # >
1896            !!!cp (162);
1897            $self->{state} = DATA_STATE;
1898          !!!next-input-character;          !!!next-input-character;
1899    
1900          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          !!!emit ($self->{current_token}); # DOCTYPE
1901    
1902          redo A;          redo A;
1903        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1904          !!!parse-error (type => 'no DOCTYPE name');          !!!cp (163);
1905          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
1906            $self->{state} = DATA_STATE;
1907            ## reconsume
1908    
1909            $self->{current_token}->{quirks} = 1;
1910            !!!emit ($self->{current_token}); # DOCTYPE
1911    
1912            redo A;
1913          } else {
1914            !!!cp (164);
1915            $self->{current_token}->{name}
1916              .= chr ($self->{next_char}); # DOCTYPE
1917            ## Stay in the state
1918            !!!next-input-character;
1919            redo A;
1920          }
1921        } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
1922          if ($self->{next_char} == 0x0009 or # HT
1923              $self->{next_char} == 0x000A or # LF
1924              $self->{next_char} == 0x000B or # VT
1925              $self->{next_char} == 0x000C or # FF
1926              $self->{next_char} == 0x0020) { # SP
1927            !!!cp (165);
1928            ## Stay in the state
1929            !!!next-input-character;
1930            redo A;
1931          } elsif ($self->{next_char} == 0x003E) { # >
1932            !!!cp (166);
1933            $self->{state} = DATA_STATE;
1934            !!!next-input-character;
1935    
1936            !!!emit ($self->{current_token}); # DOCTYPE
1937    
1938            redo A;
1939          } elsif ($self->{next_char} == -1) {
1940            !!!cp (167);
1941            !!!parse-error (type => 'unclosed DOCTYPE');
1942            $self->{state} = DATA_STATE;
1943            ## reconsume
1944    
1945            $self->{current_token}->{quirks} = 1;
1946            !!!emit ($self->{current_token}); # DOCTYPE
1947    
1948            redo A;
1949          } elsif ($self->{next_char} == 0x0050 or # P
1950                   $self->{next_char} == 0x0070) { # p
1951            !!!next-input-character;
1952            if ($self->{next_char} == 0x0055 or # U
1953                $self->{next_char} == 0x0075) { # u
1954              !!!next-input-character;
1955              if ($self->{next_char} == 0x0042 or # B
1956                  $self->{next_char} == 0x0062) { # b
1957                !!!next-input-character;
1958                if ($self->{next_char} == 0x004C or # L
1959                    $self->{next_char} == 0x006C) { # l
1960                  !!!next-input-character;
1961                  if ($self->{next_char} == 0x0049 or # I
1962                      $self->{next_char} == 0x0069) { # i
1963                    !!!next-input-character;
1964                    if ($self->{next_char} == 0x0043 or # C
1965                        $self->{next_char} == 0x0063) { # c
1966                      !!!cp (168);
1967                      $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1968                      !!!next-input-character;
1969                      redo A;
1970                    } else {
1971                      !!!cp (169);
1972                    }
1973                  } else {
1974                    !!!cp (170);
1975                  }
1976                } else {
1977                  !!!cp (171);
1978                }
1979              } else {
1980                !!!cp (172);
1981              }
1982            } else {
1983              !!!cp (173);
1984            }
1985    
1986            #
1987          } elsif ($self->{next_char} == 0x0053 or # S
1988                   $self->{next_char} == 0x0073) { # s
1989            !!!next-input-character;
1990            if ($self->{next_char} == 0x0059 or # Y
1991                $self->{next_char} == 0x0079) { # y
1992              !!!next-input-character;
1993              if ($self->{next_char} == 0x0053 or # S
1994                  $self->{next_char} == 0x0073) { # s
1995                !!!next-input-character;
1996                if ($self->{next_char} == 0x0054 or # T
1997                    $self->{next_char} == 0x0074) { # t
1998                  !!!next-input-character;
1999                  if ($self->{next_char} == 0x0045 or # E
2000                      $self->{next_char} == 0x0065) { # e
2001                    !!!next-input-character;
2002                    if ($self->{next_char} == 0x004D or # M
2003                        $self->{next_char} == 0x006D) { # m
2004                      !!!cp (174);
2005                      $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2006                      !!!next-input-character;
2007                      redo A;
2008                    } else {
2009                      !!!cp (175);
2010                    }
2011                  } else {
2012                    !!!cp (176);
2013                  }
2014                } else {
2015                  !!!cp (177);
2016                }
2017              } else {
2018                !!!cp (178);
2019              }
2020            } else {
2021              !!!cp (179);
2022            }
2023    
2024            #
2025          } else {
2026            !!!cp (180);
2027            !!!next-input-character;
2028            #
2029          }
2030    
2031          !!!parse-error (type => 'string after DOCTYPE name');
2032          $self->{current_token}->{quirks} = 1;
2033    
2034          $self->{state} = BOGUS_DOCTYPE_STATE;
2035          # next-input-character is already done
2036          redo A;
2037        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2038          if ({
2039                0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2040                #0x000D => 1, # HT, LF, VT, FF, SP, CR
2041              }->{$self->{next_char}}) {
2042            !!!cp (181);
2043            ## Stay in the state
2044            !!!next-input-character;
2045            redo A;
2046          } elsif ($self->{next_char} eq 0x0022) { # "
2047            !!!cp (182);
2048            $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2049            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2050            !!!next-input-character;
2051            redo A;
2052          } elsif ($self->{next_char} eq 0x0027) { # '
2053            !!!cp (183);
2054            $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2055            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2056            !!!next-input-character;
2057            redo A;
2058          } elsif ($self->{next_char} eq 0x003E) { # >
2059            !!!cp (184);
2060            !!!parse-error (type => 'no PUBLIC literal');
2061    
2062            $self->{state} = DATA_STATE;
2063            !!!next-input-character;
2064    
2065            $self->{current_token}->{quirks} = 1;
2066            !!!emit ($self->{current_token}); # DOCTYPE
2067    
2068            redo A;
2069          } elsif ($self->{next_char} == -1) {
2070            !!!cp (185);
2071            !!!parse-error (type => 'unclosed DOCTYPE');
2072    
2073            $self->{state} = DATA_STATE;
2074          ## reconsume          ## reconsume
2075    
2076          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          $self->{current_token}->{quirks} = 1;
2077            !!!emit ($self->{current_token}); # DOCTYPE
2078    
2079          redo A;          redo A;
2080        } else {        } else {
2081          $self->{current_token} = {type => 'DOCTYPE',          !!!cp (186);
2082                            name => chr ($self->{next_input_character}),          !!!parse-error (type => 'string after PUBLIC');
2083                            error => 1};          $self->{current_token}->{quirks} = 1;
2084          $self->{state} = 'DOCTYPE name';  
2085            $self->{state} = BOGUS_DOCTYPE_STATE;
2086          !!!next-input-character;          !!!next-input-character;
2087          redo A;          redo A;
2088        }        }
2089      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2090        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0022) { # "
2091            $self->{next_input_character} == 0x000A or # LF          !!!cp (187);
2092            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
         $self->{state} = 'after DOCTYPE name';  
2093          !!!next-input-character;          !!!next-input-character;
2094          redo A;          redo A;
2095        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2096          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          !!!cp (188);
2097          $self->{state} = 'data';          !!!parse-error (type => 'unclosed PUBLIC literal');
2098    
2099            $self->{state} = DATA_STATE;
2100          !!!next-input-character;          !!!next-input-character;
2101    
2102            $self->{current_token}->{quirks} = 1;
2103          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
         undef $self->{current_token};  
2104    
2105          redo A;          redo A;
2106        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{next_char} == -1) {
2107                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (189);
2108          $self->{current_token}->{name} .= chr ($self->{next_input_character} - 0x0020); # DOCTYPE          !!!parse-error (type => 'unclosed PUBLIC literal');
2109          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');  
2110            $self->{state} = DATA_STATE;
2111            ## reconsume
2112    
2113            $self->{current_token}->{quirks} = 1;
2114            !!!emit ($self->{current_token}); # DOCTYPE
2115    
2116            redo A;
2117          } else {
2118            !!!cp (190);
2119            $self->{current_token}->{public_identifier} # DOCTYPE
2120                .= chr $self->{next_char};
2121          ## Stay in the state          ## Stay in the state
2122          !!!next-input-character;          !!!next-input-character;
2123          redo A;          redo A;
2124        } elsif ($self->{next_input_character} == -1) {        }
2125        } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2126          if ($self->{next_char} == 0x0027) { # '
2127            !!!cp (191);
2128            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2129            !!!next-input-character;
2130            redo A;
2131          } elsif ($self->{next_char} == 0x003E) { # >
2132            !!!cp (192);
2133            !!!parse-error (type => 'unclosed PUBLIC literal');
2134    
2135            $self->{state} = DATA_STATE;
2136            !!!next-input-character;
2137    
2138            $self->{current_token}->{quirks} = 1;
2139            !!!emit ($self->{current_token}); # DOCTYPE
2140    
2141            redo A;
2142          } elsif ($self->{next_char} == -1) {
2143            !!!cp (193);
2144            !!!parse-error (type => 'unclosed PUBLIC literal');
2145    
2146            $self->{state} = DATA_STATE;
2147            ## reconsume
2148    
2149            $self->{current_token}->{quirks} = 1;
2150            !!!emit ($self->{current_token}); # DOCTYPE
2151    
2152            redo A;
2153          } else {
2154            !!!cp (194);
2155            $self->{current_token}->{public_identifier} # DOCTYPE
2156                .= chr $self->{next_char};
2157            ## Stay in the state
2158            !!!next-input-character;
2159            redo A;
2160          }
2161        } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2162          if ({
2163                0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2164                #0x000D => 1, # HT, LF, VT, FF, SP, CR
2165              }->{$self->{next_char}}) {
2166            !!!cp (195);
2167            ## Stay in the state
2168            !!!next-input-character;
2169            redo A;
2170          } elsif ($self->{next_char} == 0x0022) { # "
2171            !!!cp (196);
2172            $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2173            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2174            !!!next-input-character;
2175            redo A;
2176          } elsif ($self->{next_char} == 0x0027) { # '
2177            !!!cp (197);
2178            $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2179            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2180            !!!next-input-character;
2181            redo A;
2182          } elsif ($self->{next_char} == 0x003E) { # >
2183            !!!cp (198);
2184            $self->{state} = DATA_STATE;
2185            !!!next-input-character;
2186    
2187            !!!emit ($self->{current_token}); # DOCTYPE
2188    
2189            redo A;
2190          } elsif ($self->{next_char} == -1) {
2191            !!!cp (199);
2192          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2193          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
2194          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2195          ## reconsume          ## reconsume
2196    
2197          !!!emit ($self->{current_token});          $self->{current_token}->{quirks} = 1;
2198          undef $self->{current_token};          !!!emit ($self->{current_token}); # DOCTYPE
2199    
2200          redo A;          redo A;
2201        } else {        } else {
2202          $self->{current_token}->{name}          !!!cp (200);
2203            .= chr ($self->{next_input_character}); # DOCTYPE          !!!parse-error (type => 'string after PUBLIC literal');
2204          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');          $self->{current_token}->{quirks} = 1;
2205    
2206            $self->{state} = BOGUS_DOCTYPE_STATE;
2207            !!!next-input-character;
2208            redo A;
2209          }
2210        } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2211          if ({
2212                0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2213                #0x000D => 1, # HT, LF, VT, FF, SP, CR
2214              }->{$self->{next_char}}) {
2215            !!!cp (201);
2216            ## Stay in the state
2217            !!!next-input-character;
2218            redo A;
2219          } elsif ($self->{next_char} == 0x0022) { # "
2220            !!!cp (202);
2221            $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2222            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2223            !!!next-input-character;
2224            redo A;
2225          } elsif ($self->{next_char} == 0x0027) { # '
2226            !!!cp (203);
2227            $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2228            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2229            !!!next-input-character;
2230            redo A;
2231          } elsif ($self->{next_char} == 0x003E) { # >
2232            !!!cp (204);
2233            !!!parse-error (type => 'no SYSTEM literal');
2234            $self->{state} = DATA_STATE;
2235            !!!next-input-character;
2236    
2237            $self->{current_token}->{quirks} = 1;
2238            !!!emit ($self->{current_token}); # DOCTYPE
2239    
2240            redo A;
2241          } elsif ($self->{next_char} == -1) {
2242            !!!cp (205);
2243            !!!parse-error (type => 'unclosed DOCTYPE');
2244    
2245            $self->{state} = DATA_STATE;
2246            ## reconsume
2247    
2248            $self->{current_token}->{quirks} = 1;
2249            !!!emit ($self->{current_token}); # DOCTYPE
2250    
2251            redo A;
2252          } else {
2253            !!!cp (206);
2254            !!!parse-error (type => 'string after SYSTEM');
2255            $self->{current_token}->{quirks} = 1;
2256    
2257            $self->{state} = BOGUS_DOCTYPE_STATE;
2258            !!!next-input-character;
2259            redo A;
2260          }
2261        } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2262          if ($self->{next_char} == 0x0022) { # "
2263            !!!cp (207);
2264            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2265            !!!next-input-character;
2266            redo A;
2267          } elsif ($self->{next_char} == 0x003E) { # >
2268            !!!cp (208);
2269            !!!parse-error (type => 'unclosed PUBLIC literal');
2270    
2271            $self->{state} = DATA_STATE;
2272            !!!next-input-character;
2273    
2274            $self->{current_token}->{quirks} = 1;
2275            !!!emit ($self->{current_token}); # DOCTYPE
2276    
2277            redo A;
2278          } elsif ($self->{next_char} == -1) {
2279            !!!cp (209);
2280            !!!parse-error (type => 'unclosed SYSTEM literal');
2281    
2282            $self->{state} = DATA_STATE;
2283            ## reconsume
2284    
2285            $self->{current_token}->{quirks} = 1;
2286            !!!emit ($self->{current_token}); # DOCTYPE
2287    
2288            redo A;
2289          } else {
2290            !!!cp (210);
2291            $self->{current_token}->{system_identifier} # DOCTYPE
2292                .= chr $self->{next_char};
2293          ## Stay in the state          ## Stay in the state
2294          !!!next-input-character;          !!!next-input-character;
2295          redo A;          redo A;
2296        }        }
2297      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2298        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0027) { # '
2299            $self->{next_input_character} == 0x000A or # LF          !!!cp (211);
2300            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2301            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
2302            $self->{next_input_character} == 0x0020) { # SP          redo A;
2303          } elsif ($self->{next_char} == 0x003E) { # >
2304            !!!cp (212);
2305            !!!parse-error (type => 'unclosed PUBLIC literal');
2306    
2307            $self->{state} = DATA_STATE;
2308            !!!next-input-character;
2309    
2310            $self->{current_token}->{quirks} = 1;
2311            !!!emit ($self->{current_token}); # DOCTYPE
2312    
2313            redo A;
2314          } elsif ($self->{next_char} == -1) {
2315            !!!cp (213);
2316            !!!parse-error (type => 'unclosed SYSTEM literal');
2317    
2318            $self->{state} = DATA_STATE;
2319            ## reconsume
2320    
2321            $self->{current_token}->{quirks} = 1;
2322            !!!emit ($self->{current_token}); # DOCTYPE
2323    
2324            redo A;
2325          } else {
2326            !!!cp (214);
2327            $self->{current_token}->{system_identifier} # DOCTYPE
2328                .= chr $self->{next_char};
2329          ## Stay in the state          ## Stay in the state
2330          !!!next-input-character;          !!!next-input-character;
2331          redo A;          redo A;
2332        } elsif ($self->{next_input_character} == 0x003E) { # >        }
2333          $self->{state} = 'data';      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2334          if ({
2335                0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2336                #0x000D => 1, # HT, LF, VT, FF, SP, CR
2337              }->{$self->{next_char}}) {
2338            !!!cp (215);
2339            ## Stay in the state
2340            !!!next-input-character;
2341            redo A;
2342          } elsif ($self->{next_char} == 0x003E) { # >
2343            !!!cp (216);
2344            $self->{state} = DATA_STATE;
2345          !!!next-input-character;          !!!next-input-character;
2346    
2347          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
         undef $self->{current_token};  
2348    
2349          redo A;          redo A;
2350        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2351            !!!cp (217);
2352          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2353          $self->{state} = 'data';  
2354            $self->{state} = DATA_STATE;
2355          ## reconsume          ## reconsume
2356    
2357            $self->{current_token}->{quirks} = 1;
2358          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
         undef $self->{current_token};  
2359    
2360          redo A;          redo A;
2361        } else {        } else {
2362          !!!parse-error (type => 'string after DOCTYPE name');          !!!cp (218);
2363          $self->{current_token}->{error} = 1; # DOCTYPE          !!!parse-error (type => 'string after SYSTEM literal');
2364          $self->{state} = 'bogus DOCTYPE';          #$self->{current_token}->{quirks} = 1;
2365    
2366            $self->{state} = BOGUS_DOCTYPE_STATE;
2367          !!!next-input-character;          !!!next-input-character;
2368          redo A;          redo A;
2369        }        }
2370      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2371        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
2372          $self->{state} = 'data';          !!!cp (219);
2373            $self->{state} = DATA_STATE;
2374          !!!next-input-character;          !!!next-input-character;
2375    
2376          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
         undef $self->{current_token};  
2377    
2378          redo A;          redo A;
2379        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2380            !!!cp (220);
2381          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2382          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2383          ## reconsume          ## reconsume
2384    
2385          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
         undef $self->{current_token};  
2386    
2387          redo A;          redo A;
2388        } else {        } else {
2389            !!!cp (221);
2390          ## Stay in the state          ## Stay in the state
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
# Line 1449  sub _get_next_token ($) { Line 2399  sub _get_next_token ($) {
2399    die "$0: _get_next_token: unexpected case";    die "$0: _get_next_token: unexpected case";
2400  } # _get_next_token  } # _get_next_token
2401    
2402  sub _tokenize_attempt_to_consume_an_entity ($) {  sub _tokenize_attempt_to_consume_an_entity ($$$) {
2403    my $self = shift;    my ($self, $in_attr, $additional) = @_;
2404      
2405    if ($self->{next_input_character} == 0x0023) { # #    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
2406    
2407      if ({
2408           0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
2409           0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
2410           $additional => 1,
2411          }->{$self->{next_char}}) {
2412        !!!cp (1001);
2413        ## Don't consume
2414        ## No error
2415        return undef;
2416      } elsif ($self->{next_char} == 0x0023) { # #
2417      !!!next-input-character;      !!!next-input-character;
2418      my $num;      if ($self->{next_char} == 0x0078 or # x
2419      if ($self->{next_input_character} == 0x0078 or # x          $self->{next_char} == 0x0058) { # X
2420          $self->{next_input_character} == 0x0058) { # X        my $code;
2421        X: {        X: {
2422          my $x_char = $self->{next_input_character};          my $x_char = $self->{next_char};
2423          !!!next-input-character;          !!!next-input-character;
2424          if (0x0030 <= $self->{next_input_character} and          if (0x0030 <= $self->{next_char} and
2425              $self->{next_input_character} <= 0x0039) { # 0..9              $self->{next_char} <= 0x0039) { # 0..9
2426            $num ||= 0;            !!!cp (1002);
2427            $num *= 0x10;            $code ||= 0;
2428            $num += $self->{next_input_character} - 0x0030;            $code *= 0x10;
2429              $code += $self->{next_char} - 0x0030;
2430            redo X;            redo X;
2431          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_char} and
2432                   $self->{next_input_character} <= 0x0066) { # a..f                   $self->{next_char} <= 0x0066) { # a..f
2433            ## ISSUE: the spec says U+0078, which is apparently incorrect            !!!cp (1003);
2434            $num ||= 0;            $code ||= 0;
2435            $num *= 0x10;            $code *= 0x10;
2436            $num += $self->{next_input_character} - 0x0060 + 9;            $code += $self->{next_char} - 0x0060 + 9;
2437            redo X;            redo X;
2438          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_char} and
2439                   $self->{next_input_character} <= 0x0046) { # A..F                   $self->{next_char} <= 0x0046) { # A..F
2440            ## ISSUE: the spec says U+0058, which is apparently incorrect            !!!cp (1004);
2441            $num ||= 0;            $code ||= 0;
2442            $num *= 0x10;            $code *= 0x10;
2443            $num += $self->{next_input_character} - 0x0040 + 9;            $code += $self->{next_char} - 0x0040 + 9;
2444            redo X;            redo X;
2445          } elsif (not defined $num) { # no hexadecimal digit          } elsif (not defined $code) { # no hexadecimal digit
2446            !!!parse-error (type => 'bare hcro');            !!!cp (1005);
2447            $self->{next_input_character} = 0x0023; # #            !!!parse-error (type => 'bare hcro', line => $l, column => $c);
2448            !!!back-next-input-character ($x_char);            !!!back-next-input-character ($x_char, $self->{next_char});
2449              $self->{next_char} = 0x0023; # #
2450            return undef;            return undef;
2451          } elsif ($self->{next_input_character} == 0x003B) { # ;          } elsif ($self->{next_char} == 0x003B) { # ;
2452              !!!cp (1006);
2453            !!!next-input-character;            !!!next-input-character;
2454          } else {          } else {
2455            !!!parse-error (type => 'no refc');            !!!cp (1007);
2456              !!!parse-error (type => 'no refc', line => $l, column => $c);
2457          }          }
2458    
2459          ## TODO: check the definition for |a valid Unicode character|.          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2460          if ($num > 1114111 or $num == 0) {            !!!cp (1008);
2461            $num = 0xFFFD; # REPLACEMENT CHARACTER            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2462            ## ISSUE: Why this is not an error?            $code = 0xFFFD;
2463          }          } elsif ($code > 0x10FFFF) {
2464              !!!cp (1009);
2465          return {type => 'character', data => chr $num};            !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2466              $code = 0xFFFD;
2467            } elsif ($code == 0x000D) {
2468              !!!cp (1010);
2469              !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2470              $code = 0x000A;
2471            } elsif (0x80 <= $code and $code <= 0x9F) {
2472              !!!cp (1011);
2473              !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2474              $code = $c1_entity_char->{$code};
2475            }
2476    
2477            return {type => CHARACTER_TOKEN, data => chr $code,
2478                    has_reference => 1,
2479                    line => $l, column => $c,
2480                   };
2481        } # X        } # X
2482      } elsif (0x0030 <= $self->{next_input_character} and      } elsif (0x0030 <= $self->{next_char} and
2483               $self->{next_input_character} <= 0x0039) { # 0..9               $self->{next_char} <= 0x0039) { # 0..9
2484        my $code = $self->{next_input_character} - 0x0030;        my $code = $self->{next_char} - 0x0030;
2485        !!!next-input-character;        !!!next-input-character;
2486                
2487        while (0x0030 <= $self->{next_input_character} and        while (0x0030 <= $self->{next_char} and
2488                  $self->{next_input_character} <= 0x0039) { # 0..9                  $self->{next_char} <= 0x0039) { # 0..9
2489            !!!cp (1012);
2490          $code *= 10;          $code *= 10;
2491          $code += $self->{next_input_character} - 0x0030;          $code += $self->{next_char} - 0x0030;
2492                    
2493          !!!next-input-character;          !!!next-input-character;
2494        }        }
2495    
2496        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_char} == 0x003B) { # ;
2497            !!!cp (1013);
2498          !!!next-input-character;          !!!next-input-character;
2499        } else {        } else {
2500          !!!parse-error (type => 'no refc');          !!!cp (1014);
2501            !!!parse-error (type => 'no refc', line => $l, column => $c);
2502        }        }
2503    
2504        ## TODO: check the definition for |a valid Unicode character|.        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2505        if ($code > 1114111 or $code == 0) {          !!!cp (1015);
2506          $code = 0xFFFD; # REPLACEMENT CHARACTER          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2507          ## ISSUE: Why this is not an error?          $code = 0xFFFD;
2508          } elsif ($code > 0x10FFFF) {
2509            !!!cp (1016);
2510            !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2511            $code = 0xFFFD;
2512          } elsif ($code == 0x000D) {
2513            !!!cp (1017);
2514            !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2515            $code = 0x000A;
2516          } elsif (0x80 <= $code and $code <= 0x9F) {
2517            !!!cp (1018);
2518            !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2519            $code = $c1_entity_char->{$code};
2520        }        }
2521                
2522        return {type => 'character', data => chr $code};        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
2523                  line => $l, column => $c,
2524                 };
2525      } else {      } else {
2526        !!!parse-error (type => 'bare nero');        !!!cp (1019);
2527        !!!back-next-input-character ($self->{next_input_character});        !!!parse-error (type => 'bare nero', line => $l, column => $c);
2528        $self->{next_input_character} = 0x0023; # #        !!!back-next-input-character ($self->{next_char});
2529          $self->{next_char} = 0x0023; # #
2530        return undef;        return undef;
2531      }      }
2532    } elsif ((0x0041 <= $self->{next_input_character} and    } elsif ((0x0041 <= $self->{next_char} and
2533              $self->{next_input_character} <= 0x005A) or              $self->{next_char} <= 0x005A) or
2534             (0x0061 <= $self->{next_input_character} and             (0x0061 <= $self->{next_char} and
2535              $self->{next_input_character} <= 0x007A)) {              $self->{next_char} <= 0x007A)) {
2536      my $entity_name = chr $self->{next_input_character};      my $entity_name = chr $self->{next_char};
2537      !!!next-input-character;      !!!next-input-character;
2538    
2539      my $value = $entity_name;      my $value = $entity_name;
2540      my $match;      my $match = 0;
2541        require Whatpm::_NamedEntityList;
2542        our $EntityChar;
2543    
2544      while (length $entity_name < 10 and      while (length $entity_name < 10 and
2545             ## NOTE: Some number greater than the maximum length of entity name             ## NOTE: Some number greater than the maximum length of entity name
2546             ((0x0041 <= $self->{next_input_character} and             ((0x0041 <= $self->{next_char} and # a
2547               $self->{next_input_character} <= 0x005A) or               $self->{next_char} <= 0x005A) or # x
2548              (0x0061 <= $self->{next_input_character} and              (0x0061 <= $self->{next_char} and # a
2549               $self->{next_input_character} <= 0x007A) or               $self->{next_char} <= 0x007A) or # z
2550              (0x0030 <= $self->{next_input_character} and              (0x0030 <= $self->{next_char} and # 0
2551               $self->{next_input_character} <= 0x0039))) {               $self->{next_char} <= 0x0039) or # 9
2552        $entity_name .= chr $self->{next_input_character};              $self->{next_char} == 0x003B)) { # ;
2553        if (defined $entity_char->{$entity_name}) {        $entity_name .= chr $self->{next_char};
2554          $value = $entity_char->{$entity_name};        if (defined $EntityChar->{$entity_name}) {
2555          $match = 1;          if ($self->{next_char} == 0x003B) { # ;
2556              !!!cp (1020);
2557              $value = $EntityChar->{$entity_name};
2558              $match = 1;
2559              !!!next-input-character;
2560              last;
2561            } else {
2562              !!!cp (1021);
2563              $value = $EntityChar->{$entity_name};
2564              $match = -1;
2565              !!!next-input-character;
2566            }
2567        } else {        } else {
2568          $value .= chr $self->{next_input_character};          !!!cp (1022);
2569            $value .= chr $self->{next_char};
2570            $match *= 2;
2571            !!!next-input-character;
2572        }        }
       !!!next-input-character;  
2573      }      }
2574            
2575      if ($match) {      if ($match > 0) {
2576        if ($self->{next_input_character} == 0x003B) { # ;        !!!cp (1023);
2577          !!!next-input-character;        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2578                  line => $l, column => $c,
2579                 };
2580        } elsif ($match < 0) {
2581          !!!parse-error (type => 'no refc', line => $l, column => $c);
2582          if ($in_attr and $match < -1) {
2583            !!!cp (1024);
2584            return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
2585                    line => $l, column => $c,
2586                   };
2587        } else {        } else {
2588          !!!parse-error (type => 'refc');          !!!cp (1025);
2589            return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2590                    line => $l, column => $c,
2591                   };
2592        }        }
   
       return {type => 'character', data => $value};  
2593      } else {      } else {
2594        !!!parse-error (type => 'bare ero');        !!!cp (1026);
2595        ## NOTE: No characters are consumed in the spec.        !!!parse-error (type => 'bare ero', line => $l, column => $c);
2596        !!!back-token ({type => 'character', data => $value});        ## NOTE: "No characters are consumed" in the spec.
2597        return undef;        return {type => CHARACTER_TOKEN, data => '&'.$value,
2598                  line => $l, column => $c,
2599                 };
2600      }      }
2601    } else {    } else {
2602        !!!cp (1027);
2603      ## no characters are consumed      ## no characters are consumed
2604      !!!parse-error (type => 'bare ero');      !!!parse-error (type => 'bare ero', line => $l, column => $c);
2605      return undef;      return undef;
2606    }    }
2607  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 1586  sub _initialize_tree_constructor ($) { Line 2612  sub _initialize_tree_constructor ($) {
2612    $self->{document}->strict_error_checking (0);    $self->{document}->strict_error_checking (0);
2613    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
2614    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
2615    ## TODO: Mark the Document as an HTML document # MUST    $self->{document}->manakai_is_html (1); # MUST
2616  } # _initialize_tree_constructor  } # _initialize_tree_constructor
2617    
2618  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1613  sub _construct_tree ($) { Line 2639  sub _construct_tree ($) {
2639        
2640    !!!next-token;    !!!next-token;
2641    
   $self->{insertion_mode} = 'before head';  
2642    undef $self->{form_element};    undef $self->{form_element};
2643    undef $self->{head_element};    undef $self->{head_element};
2644    $self->{open_elements} = [];    $self->{open_elements} = [];
2645    undef $self->{inner_html_node};    undef $self->{inner_html_node};
2646    
2647      ## NOTE: The "initial" insertion mode.
2648    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
2649    
2650      ## NOTE: The "before html" insertion mode.
2651    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
2652      $self->{insertion_mode} = BEFORE_HEAD_IM;
2653    
2654      ## NOTE: The "before head" insertion mode and so on.
2655    $self->_tree_construction_main;    $self->_tree_construction_main;
2656  } # _construct_tree  } # _construct_tree
2657    
2658  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
2659    my $self = shift;    my $self = shift;
2660    B: {  
2661        if ($token->{type} eq 'DOCTYPE') {    ## NOTE: "initial" insertion mode
2662          if ($token->{error}) {  
2663            ## ISSUE: Spec currently left this case undefined.    INITIAL: {
2664            !!!parse-error (type => 'bogus DOCTYPE');      if ($token->{type} == DOCTYPE_TOKEN) {
2665          }        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
2666          my $doctype = $self->{document}->create_document_type_definition        ## error, switch to a conformance checking mode for another
2667            ($token->{name});        ## language.
2668          $self->{document}->append_child ($doctype);        my $doctype_name = $token->{name};
2669          #$phase = 'root element';        $doctype_name = '' unless defined $doctype_name;
2670          !!!next-token;        $doctype_name =~ tr/a-z/A-Z/;
2671          #redo B;        if (not defined $token->{name} or # <!DOCTYPE>
2672          return;            defined $token->{public_identifier} or
2673        } elsif ({            defined $token->{system_identifier}) {
2674                  comment => 1,          !!!cp ('t1');
2675                  'start tag' => 1,          !!!parse-error (type => 'not HTML5', token => $token);
2676                  'end tag' => 1,        } elsif ($doctype_name ne 'HTML') {
2677                  'end-of-file' => 1,          !!!cp ('t2');
2678                 }->{$token->{type}}) {          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)
2679          ## ISSUE: Spec currently left this case undefined.          !!!parse-error (type => 'not HTML5', token => $token);
2680          !!!parse-error (type => 'missing DOCTYPE');        } else {
2681          #$phase = 'root element';          !!!cp ('t3');
2682          ## reprocess        }
2683          #redo B;        
2684          return;        my $doctype = $self->{document}->create_document_type_definition
2685        } elsif ($token->{type} eq 'character') {          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
2686          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {        ## NOTE: Default value for both |public_id| and |system_id| attributes
2687            $self->{document}->manakai_append_text ($1);        ## are empty strings, so that we don't set any value in missing cases.
2688            ## ISSUE: DOM3 Core does not allow Document > Text        $doctype->public_id ($token->{public_identifier})
2689            unless (length $token->{data}) {            if defined $token->{public_identifier};
2690              ## Stay in the phase        $doctype->system_id ($token->{system_identifier})
2691              !!!next-token;            if defined $token->{system_identifier};
2692              redo B;        ## NOTE: Other DocumentType attributes are null or empty lists.
2693          ## ISSUE: internalSubset = null??
2694          $self->{document}->append_child ($doctype);
2695          
2696          if ($token->{quirks} or $doctype_name ne 'HTML') {
2697            !!!cp ('t4');
2698            $self->{document}->manakai_compat_mode ('quirks');
2699          } elsif (defined $token->{public_identifier}) {
2700            my $pubid = $token->{public_identifier};
2701            $pubid =~ tr/a-z/A-z/;
2702            if ({
2703              "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,
2704              "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
2705              "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,
2706              "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,
2707              "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,
2708              "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,
2709              "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,
2710              "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,
2711              "-//IETF//DTD HTML 2.0//EN" => 1,
2712              "-//IETF//DTD HTML 2.1E//EN" => 1,
2713              "-//IETF//DTD HTML 3.0//EN" => 1,
2714              "-//IETF//DTD HTML 3.0//EN//" => 1,
2715              "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,
2716              "-//IETF//DTD HTML 3.2//EN" => 1,
2717              "-//IETF//DTD HTML 3//EN" => 1,
2718              "-//IETF//DTD HTML LEVEL 0//EN" => 1,
2719              "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,
2720              "-//IETF//DTD HTML LEVEL 1//EN" => 1,
2721              "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,
2722              "-//IETF//DTD HTML LEVEL 2//EN" => 1,
2723              "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,
2724              "-//IETF//DTD HTML LEVEL 3//EN" => 1,
2725              "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,
2726              "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,
2727              "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,
2728              "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,
2729              "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,
2730              "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,
2731              "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,
2732              "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,
2733              "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,
2734              "-//IETF//DTD HTML STRICT//EN" => 1,
2735              "-//IETF//DTD HTML STRICT//EN//2.0" => 1,
2736              "-//IETF//DTD HTML STRICT//EN//3.0" => 1,
2737              "-//IETF//DTD HTML//EN" => 1,
2738              "-//IETF//DTD HTML//EN//2.0" => 1,
2739              "-//IETF//DTD HTML//EN//3.0" => 1,
2740              "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,
2741              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,
2742              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,
2743              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,
2744              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,
2745              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,
2746              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,
2747              "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,
2748              "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,
2749              "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,
2750              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,
2751              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,
2752              "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,
2753              "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,
2754              "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,
2755              "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,
2756              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,
2757              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,
2758              "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,
2759              "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,
2760              "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,
2761              "-//W3C//DTD HTML 3.2//EN" => 1,
2762              "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,
2763              "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,
2764              "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,
2765              "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,
2766              "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,
2767              "-//W3C//DTD W3 HTML//EN" => 1,
2768              "-//W3O//DTD W3 HTML 3.0//EN" => 1,
2769              "-//W3O//DTD W3 HTML 3.0//EN//" => 1,
2770              "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,
2771              "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,
2772              "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,
2773              "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,
2774              "HTML" => 1,
2775            }->{$pubid}) {
2776              !!!cp ('t5');
2777              $self->{document}->manakai_compat_mode ('quirks');
2778            } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or
2779                     $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {
2780              if (defined $token->{system_identifier}) {
2781                !!!cp ('t6');
2782                $self->{document}->manakai_compat_mode ('quirks');
2783              } else {
2784                !!!cp ('t7');
2785                $self->{document}->manakai_compat_mode ('limited quirks');
2786            }            }
2787            } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or
2788                     $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {
2789              !!!cp ('t8');
2790              $self->{document}->manakai_compat_mode ('limited quirks');
2791            } else {
2792              !!!cp ('t9');
2793          }          }
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error (type => 'missing DOCTYPE');  
         #$phase = 'root element';  
         ## reprocess  
         #redo B;  
         return;  
2794        } else {        } else {
2795          die "$0: $token->{type}: Unknown token";          !!!cp ('t10');
2796        }        }
2797      } # B        if (defined $token->{system_identifier}) {
2798            my $sysid = $token->{system_identifier};
2799            $sysid =~ tr/A-Z/a-z/;
2800            if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
2801              ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"
2802              $self->{document}->manakai_compat_mode ('quirks');
2803              !!!cp ('t11');
2804            } else {
2805              !!!cp ('t12');
2806            }
2807          } else {
2808            !!!cp ('t13');
2809          }
2810          
2811          ## Go to the "before html" insertion mode.
2812          !!!next-token;
2813          return;
2814        } elsif ({
2815                  START_TAG_TOKEN, 1,
2816                  END_TAG_TOKEN, 1,
2817                  END_OF_FILE_TOKEN, 1,
2818                 }->{$token->{type}}) {
2819          !!!cp ('t14');
2820          !!!parse-error (type => 'no DOCTYPE', token => $token);
2821          $self->{document}->manakai_compat_mode ('quirks');
2822          ## Go to the "before html" insertion mode.
2823          ## reprocess
2824          return;
2825        } elsif ($token->{type} == CHARACTER_TOKEN) {
2826          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2827            ## Ignore the token
2828    
2829            unless (length $token->{data}) {
2830              !!!cp ('t15');
2831              ## Stay in the insertion mode.
2832              !!!next-token;
2833              redo INITIAL;
2834            } else {
2835              !!!cp ('t16');
2836            }
2837          } else {
2838            !!!cp ('t17');
2839          }
2840    
2841          !!!parse-error (type => 'no DOCTYPE', token => $token);
2842          $self->{document}->manakai_compat_mode ('quirks');
2843          ## Go to the "before html" insertion mode.
2844          ## reprocess
2845          return;
2846        } elsif ($token->{type} == COMMENT_TOKEN) {
2847          !!!cp ('t18');
2848          my $comment = $self->{document}->create_comment ($token->{data});
2849          $self->{document}->append_child ($comment);
2850          
2851          ## Stay in the insertion mode.
2852          !!!next-token;
2853          redo INITIAL;
2854        } else {
2855          die "$0: $token->{type}: Unknown token type";
2856        }
2857      } # INITIAL
2858    
2859      die "$0: _tree_construction_initial: This should be never reached";
2860  } # _tree_construction_initial  } # _tree_construction_initial
2861    
2862  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
2863    my $self = shift;    my $self = shift;
2864    
2865      ## NOTE: "before html" insertion mode.
2866        
2867    B: {    B: {
2868        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
2869          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
2870            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
2871          ## Ignore the token          ## Ignore the token
2872          ## Stay in the phase          ## Stay in the insertion mode.
2873          !!!next-token;          !!!next-token;
2874          redo B;          redo B;
2875        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
2876            !!!cp ('t20');
2877          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
2878          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
2879          ## Stay in the phase          ## Stay in the insertion mode.
2880          !!!next-token;          !!!next-token;
2881          redo B;          redo B;
2882        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
2883          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2884            $self->{document}->manakai_append_text ($1);            ## Ignore the token.
2885            ## ISSUE: DOM3 Core does not allow Document > Text  
2886            unless (length $token->{data}) {            unless (length $token->{data}) {
2887              ## Stay in the phase              !!!cp ('t21');
2888                ## Stay in the insertion mode.
2889              !!!next-token;              !!!next-token;
2890              redo B;              redo B;
2891              } else {
2892                !!!cp ('t22');
2893            }            }
2894            } else {
2895              !!!cp ('t23');
2896          }          }
2897    
2898            $self->{application_cache_selection}->(undef);
2899    
2900          #          #
2901          } elsif ($token->{type} == START_TAG_TOKEN) {
2902            if ($token->{tag_name} eq 'html') {
2903              my $root_element;
2904              !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}, $token);
2905              $self->{document}->append_child ($root_element);
2906              push @{$self->{open_elements}},
2907                  [$root_element, $el_category->{html}];
2908    
2909              if ($token->{attributes}->{manifest}) {
2910                !!!cp ('t24');
2911                $self->{application_cache_selection}
2912                    ->($token->{attributes}->{manifest}->{value});
2913                ## ISSUE: Spec is unclear on relative references.
2914                ## According to Hixie (#whatwg 2008-03-19), it should be
2915                ## resolved against the base URI of the document in HTML
2916                ## or xml:base of the element in XHTML.
2917              } else {
2918                !!!cp ('t25');
2919                $self->{application_cache_selection}->(undef);
2920              }
2921    
2922              !!!next-token;
2923              return; ## Go to the "before head" insertion mode.
2924            } else {
2925              !!!cp ('t25.1');
2926              #
2927            }
2928        } elsif ({        } elsif ({
2929                  'start tag' => 1,                  END_TAG_TOKEN, 1,
2930                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
2931                 }->{$token->{type}}) {                 }->{$token->{type}}) {
2932          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
2933          #          #
2934        } else {        } else {
2935          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
2936        }        }
2937        my $root_element; !!!create-element ($root_element, 'html');  
2938        $self->{document}->append_child ($root_element);      my $root_element; !!!create-element ($root_element, 'html',, $token);
2939        push @{$self->{open_elements}}, [$root_element, 'html'];      $self->{document}->append_child ($root_element);
2940        #$phase = 'main';      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
2941        ## reprocess  
2942        #redo B;      $self->{application_cache_selection}->(undef);
2943        return;  
2944        ## NOTE: Reprocess the token.
2945        return; ## Go to the "before head" insertion mode.
2946    
2947        ## ISSUE: There is an issue in the spec
2948    } # B    } # B
2949    
2950      die "$0: _tree_construction_root_element: This should never be reached";
2951  } # _tree_construction_root_element  } # _tree_construction_root_element
2952    
2953  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 1732  sub _reset_insertion_mode ($) { Line 2962  sub _reset_insertion_mode ($) {
2962            
2963      ## Step 3      ## Step 3
2964      S3: {      S3: {
2965        $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
2966        if (defined $self->{inner_html_node}) {          $last = 1;
2967          if ($self->{inner_html_node}->[1] eq 'td' or          if (defined $self->{inner_html_node}) {
2968              $self->{inner_html_node}->[1] eq 'th') {            if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) {
2969            #              !!!cp ('t27');
2970          } else {              #
2971            $node = $self->{inner_html_node};            } else {
2972                !!!cp ('t28');
2973                $node = $self->{inner_html_node};
2974              }
2975          }          }
2976        }        }
2977            
2978        ## Step 4..13        ## Step 4..13
2979        my $new_mode = {        my $new_mode = {
2980                        select => 'in select',                        select => IN_SELECT_IM,
2981                        td => 'in cell',                        ## NOTE: |option| and |optgroup| do not set
2982                        th => 'in cell',                        ## insertion mode to "in select" by themselves.
2983                        tr => 'in row',                        td => IN_CELL_IM,
2984                        tbody => 'in table body',                        th => IN_CELL_IM,
2985                        thead => 'in table head',                        tr => IN_ROW_IM,
2986                        tfoot => 'in table foot',                        tbody => IN_TABLE_BODY_IM,
2987                        caption => 'in caption',                        thead => IN_TABLE_BODY_IM,
2988                        colgroup => 'in column group',                        tfoot => IN_TABLE_BODY_IM,
2989                        table => 'in table',                        caption => IN_CAPTION_IM,
2990                        head => 'in body', # not in head!                        colgroup => IN_COLUMN_GROUP_IM,
2991                        body => 'in body',                        table => IN_TABLE_IM,
2992                        frameset => 'in frameset',                        head => IN_BODY_IM, # not in head!
2993                       }->{$node->[1]};                        body => IN_BODY_IM,
2994                          frameset => IN_FRAMESET_IM,
2995                         }->{$node->[0]->manakai_local_name};
2996                         ## TODO: Foreign namespace case OK?
2997        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
2998                
2999        ## Step 14        ## Step 14
3000        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3001          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3002            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3003              $self->{insertion_mode} = BEFORE_HEAD_IM;
3004          } else {          } else {
3005            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3006              !!!cp ('t30');
3007              $self->{insertion_mode} = AFTER_HEAD_IM;
3008          }          }
3009          return;          return;
3010          } else {
3011            !!!cp ('t31');
3012        }        }
3013                
3014        ## Step 15        ## Step 15
3015        $self->{insertion_mode} = 'in body' and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3016                
3017        ## Step 16        ## Step 16
3018        $i--;        $i--;
# Line 1780  sub _reset_insertion_mode ($) { Line 3021  sub _reset_insertion_mode ($) {
3021        ## Step 17        ## Step 17
3022        redo S3;        redo S3;
3023      } # S3      } # S3
3024    
3025      die "$0: _reset_insertion_mode: This line should never be reached";
3026  } # _reset_insertion_mode  } # _reset_insertion_mode
3027    
3028  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3029    my $self = shift;    my $self = shift;
3030    
   my $phase = 'main';  
   
3031    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3032    
3033    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 1803  sub _tree_construction_main ($) { Line 3044  sub _tree_construction_main ($) {
3044      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3045      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3046        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3047            !!!cp ('t32');
3048          return;          return;
3049        }        }
3050      }      }
# Line 1817  sub _tree_construction_main ($) { Line 3059  sub _tree_construction_main ($) {
3059    
3060        ## Step 6        ## Step 6
3061        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3062            !!!cp ('t33_1');
3063          #          #
3064        } else {        } else {
3065          my $in_open_elements;          my $in_open_elements;
3066          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3067            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3068                !!!cp ('t33');
3069              $in_open_elements = 1;              $in_open_elements = 1;
3070              last OE;              last OE;
3071            }            }
3072          }          }
3073          if ($in_open_elements) {          if ($in_open_elements) {
3074              !!!cp ('t34');
3075            #            #
3076          } else {          } else {
3077              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3078              !!!cp ('t35');
3079            redo S4;            redo S4;
3080          }          }
3081        }        }
# Line 1851  sub _tree_construction_main ($) { Line 3098  sub _tree_construction_main ($) {
3098    
3099        ## Step 11        ## Step 11
3100        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3101            !!!cp ('t36');
3102          ## Step 7'          ## Step 7'
3103          $i++;          $i++;
3104          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3105                    
3106          redo S7;          redo S7;
3107        }        }
3108    
3109          !!!cp ('t37');
3110      } # S7      } # S7
3111    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3112    
3113    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3114      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3115        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3116            !!!cp ('t38');
3117          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3118          return;          return;
3119        }        }
3120      }      }
3121    
3122        !!!cp ('t39');
3123    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3124    
3125    my $style_start_tag = sub {    my $insert;
3126      my $style_el; !!!create-element ($style_el, 'style');  
3127      ## $self->{insertion_mode} eq 'in head' and ... (always true)    my $parse_rcdata = sub ($) {
3128      (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})      my ($content_model_flag) = @_;
3129       ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
3130        ->append_child ($style_el);      ## Step 1
3131      $self->{content_model_flag} = 'CDATA';      my $start_tag_name = $token->{tag_name};
3132                      my $el;
3133        !!!create-element ($el, $start_tag_name, $token->{attributes}, $token);
3134    
3135        ## Step 2
3136        $insert->($el);
3137    
3138        ## Step 3
3139        $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3140        delete $self->{escape}; # MUST
3141    
3142        ## Step 4
3143      my $text = '';      my $text = '';
3144      !!!next-token;      !!!next-token;
3145      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3146          !!!cp ('t40');
3147        $text .= $token->{data};        $text .= $token->{data};
3148        !!!next-token;        !!!next-token;
3149      } # stop if non-character token or tokenizer stops tokenising      }
3150    
3151        ## Step 5
3152      if (length $text) {      if (length $text) {
3153        $style_el->manakai_append_text ($text);        !!!cp ('t41');
3154          my $text = $self->{document}->create_text_node ($text);
3155          $el->append_child ($text);
3156      }      }
3157        
3158      $self->{content_model_flag} = 'PCDATA';      ## Step 6
3159                      $self->{content_model} = PCDATA_CONTENT_MODEL;
3160      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {  
3161        ## Step 7
3162        if ($token->{type} == END_TAG_TOKEN and
3163            $token->{tag_name} eq $start_tag_name) {
3164          !!!cp ('t42');
3165        ## Ignore the token        ## Ignore the token
3166      } else {      } else {
3167        !!!parse-error (type => 'in CDATA:#'.$token->{type});        ## NOTE: An end-of-file token.
3168        ## ISSUE: And ignore?        if ($content_model_flag == CDATA_CONTENT_MODEL) {
3169            !!!cp ('t43');
3170            !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3171          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
3172            !!!cp ('t44');
3173            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
3174          } else {
3175            die "$0: $content_model_flag in parse_rcdata";
3176          }
3177      }      }
3178      !!!next-token;      !!!next-token;
3179    }; # $style_start_tag    }; # $parse_rcdata
3180    
3181    my $script_start_tag = sub {    my $script_start_tag = sub () {
3182      my $script_el;      my $script_el;
3183      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, 'script', $token->{attributes}, $token);
3184      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
3185    
3186      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
3187        delete $self->{escape}; # MUST
3188            
3189      my $text = '';      my $text = '';
3190      !!!next-token;      !!!next-token;
3191      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
3192          !!!cp ('t45');
3193        $text .= $token->{data};        $text .= $token->{data};
3194        !!!next-token;        !!!next-token;
3195      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
3196      if (length $text) {      if (length $text) {
3197          !!!cp ('t46');
3198        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
3199      }      }
3200                                
3201      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
3202    
3203      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
3204          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
3205          !!!cp ('t47');
3206        ## Ignore the token        ## Ignore the token
3207      } else {      } else {
3208        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
3209          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3210        ## ISSUE: And ignore?        ## ISSUE: And ignore?
3211        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3212      }      }
3213            
3214      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
3215          !!!cp ('t49');
3216        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3217      } else {      } else {
3218          !!!cp ('t50');
3219        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
3220        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
3221          
3222        (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})        $insert->($script_el);
        ? $self->{head_element} : $self->{open_elements}->[-1]->[0])->append_child ($script_el);  
3223                
3224        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
3225                
# Line 1943  sub _tree_construction_main ($) { Line 3229  sub _tree_construction_main ($) {
3229      !!!next-token;      !!!next-token;
3230    }; # $script_start_tag    }; # $script_start_tag
3231    
3232      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
3233      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
3234      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
3235    
3236    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
3237      my $tag_name = shift;      my $end_tag_token = shift;
3238        my $tag_name = $end_tag_token->{tag_name};
3239    
3240        ## NOTE: The adoption agency algorithm (AAA).
3241    
3242      FET: {      FET: {
3243        ## Step 1        ## Step 1
3244        my $formatting_element;        my $formatting_element;
3245        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
3246        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3247          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3248              !!!cp ('t52');
3249              last AFE;
3250            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
3251                         eq $tag_name) {
3252              !!!cp ('t51');
3253            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
3254            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
3255            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
3256          }          }
3257        } # AFE        } # AFE
3258        unless (defined $formatting_element) {        unless (defined $formatting_element) {
3259          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
3260            !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);
3261          ## Ignore the token          ## Ignore the token
3262          !!!next-token;          !!!next-token;
3263          return;          return;
# Line 1972  sub _tree_construction_main ($) { Line 3269  sub _tree_construction_main ($) {
3269          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3270          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
3271            if ($in_scope) {            if ($in_scope) {
3272                !!!cp ('t54');
3273              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
3274              last INSCOPE;              last INSCOPE;
3275            } else { # in open elements but not in scope            } else { # in open elements but not in scope
3276              !!!parse-error;              !!!cp ('t55');
3277                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3278                                token => $end_tag_token);
3279              ## Ignore the token              ## Ignore the token
3280              !!!next-token;              !!!next-token;
3281              return;              return;
3282            }            }
3283          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
3284                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
3285            $in_scope = 0;            $in_scope = 0;
3286          }          }
3287        } # INSCOPE        } # INSCOPE
3288        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
3289          !!!parse-error;          !!!cp ('t57');
3290            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3291                            token => $end_tag_token);
3292          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
3293          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
3294          return;          return;
3295        }        }
3296        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
3297          !!!parse-error;          !!!cp ('t58');
3298            !!!parse-error (type => 'not closed',
3299                            value => $self->{open_elements}->[-1]->[0]
3300                                ->manakai_local_name,
3301                            token => $end_tag_token);
3302        }        }
3303                
3304        ## Step 2        ## Step 2
# Line 2002  sub _tree_construction_main ($) { Line 3306  sub _tree_construction_main ($) {
3306        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
3307        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3308          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3309          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
3310              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
3311              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
3312               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
3313              !!!cp ('t59');
3314            $furthest_block = $node;            $furthest_block = $node;
3315            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
3316          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
3317              !!!cp ('t60');
3318            last OE;            last OE;
3319          }          }
3320        } # OE        } # OE
3321                
3322        ## Step 3        ## Step 3
3323        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
3324            !!!cp ('t61');
3325          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
3326          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
3327          !!!next-token;          !!!next-token;
# Line 2027  sub _tree_construction_main ($) { Line 3334  sub _tree_construction_main ($) {
3334        ## Step 5        ## Step 5
3335        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
3336        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
3337            !!!cp ('t62');
3338          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
3339        }        }
3340                
# Line 2049  sub _tree_construction_main ($) { Line 3357  sub _tree_construction_main ($) {
3357          S7S2: {          S7S2: {
3358            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
3359              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
3360                  !!!cp ('t63');
3361                $node_i_in_active = $_;                $node_i_in_active = $_;
3362                last S7S2;                last S7S2;
3363              }              }
# Line 2062  sub _tree_construction_main ($) { Line 3371  sub _tree_construction_main ($) {
3371                    
3372          ## Step 4          ## Step 4
3373          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
3374              !!!cp ('t64');
3375            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
3376          }          }
3377                    
3378          ## Step 5          ## Step 5
3379          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
3380              !!!cp ('t65');
3381            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
3382            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
3383            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2084  sub _tree_construction_main ($) { Line 3395  sub _tree_construction_main ($) {
3395        } # S7          } # S7  
3396                
3397        ## Step 8        ## Step 8
3398        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
3399            my $foster_parent_element;
3400            my $next_sibling;
3401            OE: for (reverse 0..$#{$self->{open_elements}}) {
3402              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
3403                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3404                                 if (defined $parent and $parent->node_type == 1) {
3405                                   !!!cp ('t65.1');
3406                                   $foster_parent_element = $parent;
3407                                   $next_sibling = $self->{open_elements}->[$_]->[0];
3408                                 } else {
3409                                   !!!cp ('t65.2');
3410                                   $foster_parent_element
3411                                     = $self->{open_elements}->[$_ - 1]->[0];
3412                                 }
3413                                 last OE;
3414                               }
3415                             } # OE
3416                             $foster_parent_element = $self->{open_elements}->[0]->[0]
3417                               unless defined $foster_parent_element;
3418            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
3419            $open_tables->[-1]->[1] = 1; # tainted
3420          } else {
3421            !!!cp ('t65.3');
3422            $common_ancestor_node->[0]->append_child ($last_node->[0]);
3423          }
3424                
3425        ## Step 9        ## Step 9
3426        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2101  sub _tree_construction_main ($) { Line 3437  sub _tree_construction_main ($) {
3437        my $i;        my $i;
3438        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3439          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
3440              !!!cp ('t66');
3441            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
3442            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
3443          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
3444              !!!cp ('t67');
3445            $i = $_;            $i = $_;
3446          }          }
3447        } # AFE        } # AFE
# Line 2113  sub _tree_construction_main ($) { Line 3451  sub _tree_construction_main ($) {
3451        undef $i;        undef $i;
3452        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3453          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
3454              !!!cp ('t68');
3455            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
3456            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
3457          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
3458              !!!cp ('t69');
3459            $i = $_;            $i = $_;
3460          }          }
3461        } # OE        } # OE
# Line 2126  sub _tree_construction_main ($) { Line 3466  sub _tree_construction_main ($) {
3466      } # FET      } # FET
3467    }; # $formatting_end_tag    }; # $formatting_end_tag
3468    
3469    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
3470      $self->{open_elements}->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
3471    }; # $insert_to_current    }; # $insert_to_current
3472    
3473    my $insert_to_foster = sub {    my $insert_to_foster = sub {
3474                         my $child = shift;      my $child = shift;
3475                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3476                              table => 1, tbody => 1, tfoot => 1,        # MUST
3477                              thead => 1, tr => 1,        my $foster_parent_element;
3478                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
3479                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
3480                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
3481                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3482                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
3483                                   !!!cp ('t70');
3484                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
3485                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
3486                               } else {                               } else {
3487                                   !!!cp ('t71');
3488                                 $foster_parent_element                                 $foster_parent_element
3489                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
3490                               }                               }
# Line 2156  sub _tree_construction_main ($) { Line 3495  sub _tree_construction_main ($) {
3495                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
3496                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
3497                             ($child, $next_sibling);                             ($child, $next_sibling);
3498                         } else {        $open_tables->[-1]->[1] = 1; # tainted
3499                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
3500                         }        !!!cp ('t72');
3501          $self->{open_elements}->[-1]->[0]->append_child ($child);
3502        }
3503    }; # $insert_to_foster    }; # $insert_to_foster
3504    
3505    my $in_body = sub {    B: {
3506      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
3507      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
3508        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);
3509          $script_start_tag->();        ## Ignore the token
3510          return;        ## Stay in the phase
3511        } elsif ($token->{tag_name} eq 'style') {        !!!next-token;
3512          $style_start_tag->();        redo B;
3513          return;      } elsif ($token->{type} == START_TAG_TOKEN and
3514        } elsif ({               $token->{tag_name} eq 'html') {
3515                  base => 1, link => 1, meta => 1,        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
3516                 }->{$token->{tag_name}}) {          !!!cp ('t79');
3517          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'after html:html', token => $token);
3518          ## NOTE: This is an "as if in head" code clone          $self->{insertion_mode} = AFTER_BODY_IM;
3519          my $el;        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
3520          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!cp ('t80');
3521          if (defined $self->{head_element}) {          !!!parse-error (type => 'after html:html', token => $token);
3522            $self->{head_element}->append_child ($el);          $self->{insertion_mode} = AFTER_FRAMESET_IM;
3523          } else {        } else {
3524            $insert->($el);          !!!cp ('t81');
3525          }        }
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'title') {  
         !!!parse-error (type => 'in body:title');  
         ## NOTE: There is an "as if in head" code clone  
         my $title_el;  
         !!!create-element ($title_el, 'title', $token->{attributes});  
         (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
           ->append_child ($title_el);  
         $self->{content_model_flag} = 'RCDATA';  
           
         my $text = '';  
         !!!next-token;  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $title_el->manakai_append_text ($text);  
         }  
           
         $self->{content_model_flag} = 'PCDATA';  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq 'title') {  
           ## Ignore the token  
         } else {  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, p => 1, ul => 1,  
                 pre => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         if ($token->{tag_name} eq 'pre') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
         } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'li') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         if (defined $i) {  
           !!!parse-error (type => 'in hn:hn');  
           splice @{$self->{open_elements}}, $i;  
         }  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error (type => 'in a:a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
   
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
   
         !!!next-token;  
         return;  
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
3526    
3527          !!!next-token;        !!!cp ('t82');
3528          return;        !!!parse-error (type => 'not first start tag', token => $token);
3529        } elsif ($token->{tag_name} eq 'marquee' or        my $top_el = $self->{open_elements}->[0]->[0];
3530                 $token->{tag_name} eq 'object') {        for my $attr_name (keys %{$token->{attributes}}) {
3531          $reconstruct_active_formatting_elements->($insert_to_current);          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
3532                      !!!cp ('t84');
3533          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            $top_el->set_attribute_ns
3534          push @$active_formatting_elements, ['#marker', ''];              (undef, [undef, $attr_name],
3535                         $token->{attributes}->{$attr_name}->{value});
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{content_model_flag} = 'CDATA';  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
3536          }          }
3537                  }
3538          $reconstruct_active_formatting_elements->($insert_to_current);        !!!next-token;
3539                  redo B;
3540          !!!insert-element-t ($token->{tag_name}, $token->{attributes});      } elsif ($token->{type} == COMMENT_TOKEN) {
3541          pop @{$self->{open_elements}};        my $comment = $self->{document}->create_comment ($token->{data});
3542                  if ($self->{insertion_mode} & AFTER_HTML_IMS) {
3543          !!!next-token;          !!!cp ('t85');
3544          return;          $self->{document}->append_child ($comment);
3545        } elsif ($token->{tag_name} eq 'hr') {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
3546          ## has a p element in scope          !!!cp ('t86');
3547          INSCOPE: for (reverse @{$self->{open_elements}}) {          $self->{open_elements}->[0]->[0]->append_child ($comment);
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                         {type => 'character',  
                          data => 'This is a searchable index. Insert your search keywords here: '}, # SHOULD  
                         ## TODO: make this configurable  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'},  
                        );  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ({  
                 textarea => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         if ($token->{tag_name} eq 'textarea') {  
           ## TODO: $self->{form_element} if defined  
           $self->{content_model_flag} = 'RCDATA';  
         } else {  
           $self->{content_model_flag} = 'CDATA';  
         }  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model_flag} = 'PCDATA';  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq $tag_name) {  
           ## Ignore the token  
         } else {  
           if ($token->{tag_name} eq 'textarea') {  
             !!!parse-error (type => 'in CDATA:#'.$token->{type});  
           } else {  
             !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
           }  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
3548        } else {        } else {
3549          $reconstruct_active_formatting_elements->($insert_to_current);          !!!cp ('t87');
3550                    $self->{open_elements}->[-1]->[0]->append_child ($comment);
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
3551        }        }
3552      } elsif ($token->{type} eq 'end tag') {        !!!next-token;
3553        if ($token->{tag_name} eq 'body') {        redo B;
3554          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {      } elsif ($self->{insertion_mode} & HEAD_IMS) {
3555            ## ISSUE: There is an issue in the spec.        if ($token->{type} == CHARACTER_TOKEN) {
3556            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3557              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3558            }              !!!cp ('t88.2');
3559            $self->{insertion_mode} = 'after body';              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3560            !!!next-token;            } else {
3561            return;              !!!cp ('t88.1');
3562          } else {              ## Ignore the token.
3563            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!next-token;
3564            ## Ignore the token              redo B;
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
           }  
           $self->{insertion_mode} = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 form => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
3565            }            }
3566          } # INSCOPE            unless (length $token->{data}) {
3567                        !!!cp ('t88');
3568          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {              !!!next-token;
3569            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              redo B;
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         undef $self->{form_element} if $token->{tag_name} eq 'form';  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
3570            }            }
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
3571          }          }
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex=> 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
3572    
3573          ## Step 2          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3574          S2: {            !!!cp ('t89');
3575            if ($node->[1] eq $token->{tag_name}) {            ## As if <head>
3576              ## Step 1            !!!create-element ($self->{head_element}, 'head',, $token);
3577              ## generate implied end tags            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3578              if ({            push @{$self->{open_elements}},
3579                   dd => 1, dt => 1, li => 1, p => 1,                [$self->{head_element}, $el_category->{head}];
3580                   td => 1, th => 1, tr => 1,  
3581                  }->{$self->{open_elements}->[-1]->[1]}) {            ## Reprocess in the "in head" insertion mode...
3582                !!!back-token;            pop @{$self->{open_elements}};
3583                $token = {type => 'end tag',  
3584                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST            ## Reprocess in the "after head" insertion mode...
3585                return;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3586              }            !!!cp ('t90');
3587                      ## As if </noscript>
3588              ## Step 2            pop @{$self->{open_elements}};
3589              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {            !!!parse-error (type => 'in noscript:#character', token => $token);
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
             }  
               
             ## Step 3  
             splice @{$self->{open_elements}}, $node_i;  
   
             !!!next-token;  
             last S2;  
           } else {  
             ## Step 3  
             if (not $formatting_category->{$node->[1]} and  
                 #not $phrasing_category->{$node->[1]} and  
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error (type => 'not closed:'.$node->[1]);  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
           }  
             
           ## Step 4  
           $node_i--;  
           $node = $self->{open_elements}->[$node_i];  
3590                        
3591            ## Step 5;            ## Reprocess in the "in head" insertion mode...
3592            redo S2;            ## As if </head>
3593          } # S2            pop @{$self->{open_elements}};
         return;  
       }  
     }  
   }; # $in_body  
3594    
3595    B: {            ## Reprocess in the "after head" insertion mode...
3596      if ($phase eq 'main') {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3597        if ($token->{type} eq 'DOCTYPE') {            !!!cp ('t91');
3598          !!!parse-error (type => 'in html:#DOCTYPE');            pop @{$self->{open_elements}};
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
         ## TODO: unless it is the first start tag token, parse-error  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
3599    
3600          ## Stop parsing            ## Reprocess in the "after head" insertion mode...
3601          last B;          } else {
3602              !!!cp ('t92');
3603            }
3604    
3605          ## ISSUE: There is an issue in the spec.          ## "after head" insertion mode
3606        } else {          ## As if <body>
3607          if ($self->{insertion_mode} eq 'before head') {          !!!insert-element ('body',, $token);
3608            if ($token->{type} eq 'character') {          $self->{insertion_mode} = IN_BODY_IM;
3609              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          ## reprocess
3610                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          redo B;
3611                unless (length $token->{data}) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3612            if ($token->{tag_name} eq 'head') {
3613              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3614                !!!cp ('t93');
3615                !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);
3616                $self->{open_elements}->[-1]->[0]->append_child
3617                    ($self->{head_element});
3618                push @{$self->{open_elements}},
3619                    [$self->{head_element}, $el_category->{head}];
3620                $self->{insertion_mode} = IN_HEAD_IM;
3621                !!!next-token;
3622                redo B;
3623                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3624                    !!!cp ('t94');
3625                    #
3626                  } else {
3627                    !!!cp ('t95');
3628                    !!!parse-error (type => 'in head:head', token => $token); # or in head noscript
3629                    ## Ignore the token
3630                  !!!next-token;                  !!!next-token;
3631                  redo B;                  redo B;
3632                }                }
3633              }              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3634              ## As if <head>                !!!cp ('t96');
             !!!create-element ($self->{head_element}, 'head');  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
3635                ## As if <head>                ## As if <head>
3636                !!!create-element ($self->{head_element}, 'head');                !!!create-element ($self->{head_element}, 'head',, $token);
3637                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3638                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                push @{$self->{open_elements}},
3639                $self->{insertion_mode} = 'in head';                    [$self->{head_element}, $el_category->{head}];
3640                ## reprocess  
3641                redo B;                $self->{insertion_mode} = IN_HEAD_IM;
3642                  ## Reprocess in the "in head" insertion mode...
3643              } else {              } else {
3644                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t97');
               ## Ignore the token  
               !!!next-token;  
               redo B;  
3645              }              }
           } else {  
             die "$0: $token->{type}: Unknown type";  
           }  
         } elsif ($self->{insertion_mode} eq 'in head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'title') {  
               ## NOTE: There is an "as if in head" code clone  
               my $title_el;  
               !!!create-element ($title_el, 'title', $token->{attributes});  
               (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
                 ->append_child ($title_el);  
               $self->{content_model_flag} = 'RCDATA';  
3646    
3647                my $text = '';              if ($token->{tag_name} eq 'base') {
3648                !!!next-token;                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3649                while ($token->{type} eq 'character') {                  !!!cp ('t98');
3650                  $text .= $token->{data};                  ## As if </noscript>
3651                  !!!next-token;                  pop @{$self->{open_elements}};
3652                }                  !!!parse-error (type => 'in noscript:base', token => $token);
               if (length $text) {  
                 $title_el->manakai_append_text ($text);  
               }  
                 
               $self->{content_model_flag} = 'PCDATA';  
3653                                
3654                if ($token->{type} eq 'end tag' and                  $self->{insertion_mode} = IN_HEAD_IM;
3655                    $token->{tag_name} eq 'title') {                  ## Reprocess in the "in head" insertion mode...
                 ## Ignore the token  
3656                } else {                } else {
3657                  !!!parse-error (type => 'in RCDATA:#'.$token->{type});                  !!!cp ('t99');
                 ## ISSUE: And ignore?  
3658                }                }
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'style') {  
               $style_start_tag->();  
               redo B;  
             } elsif ($token->{tag_name} eq 'script') {  
               $script_start_tag->();  
               redo B;  
             } elsif ({base => 1, link => 1, meta => 1}->{$token->{tag_name}}) {  
               ## NOTE: There are "as if in head" code clones  
               my $el;  
               !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
               (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
                 ->append_child ($el);  
3659    
3660                !!!next-token;                ## NOTE: There is a "as if in head" code clone.
3661                redo B;                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3662              } elsif ($token->{tag_name} eq 'head') {                  !!!cp ('t100');
3663                !!!parse-error (type => 'in head:head');                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3664                ## Ignore the token                  push @{$self->{open_elements}},
3665                !!!next-token;                      [$self->{head_element}, $el_category->{head}];
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'head') {  
               if ($self->{open_elements}->[-1]->[1] eq 'head') {  
                 pop @{$self->{open_elements}};  
3666                } else {                } else {
3667                  !!!parse-error (type => 'unmatched end tag:head');                  !!!cp ('t101');
3668                }                }
3669                $self->{insertion_mode} = 'after head';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3670                !!!next-token;                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3671                redo B;                pop @{$self->{open_elements}} # <head>
3672              } elsif ($token->{tag_name} eq 'html') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
               #  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
3673                !!!next-token;                !!!next-token;
3674                redo B;                redo B;
3675              }              } elsif ($token->{tag_name} eq 'link') {
3676            } else {                ## NOTE: There is a "as if in head" code clone.
3677              #                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3678            }                  !!!cp ('t102');
3679                    !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3680            if ($self->{open_elements}->[-1]->[1] eq 'head') {                  push @{$self->{open_elements}},
3681              ## As if </head>                      [$self->{head_element}, $el_category->{head}];
3682              pop @{$self->{open_elements}};                } else {
3683            }                  !!!cp ('t103');
           $self->{insertion_mode} = 'after head';  
           ## reprocess  
           redo B;  
   
           ## ISSUE: An issue in the spec.  
         } elsif ($self->{insertion_mode} eq 'after head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
3684                }                }
3685              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3686                              pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3687              #                pop @{$self->{open_elements}} # <head>
3688            } elsif ($token->{type} eq 'comment') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
3689                !!!next-token;                !!!next-token;
3690                redo B;                redo B;
3691              } elsif ({              } elsif ($token->{tag_name} eq 'meta') {
3692                        base => 1, link => 1, meta => 1,                ## NOTE: There is a "as if in head" code clone.
3693                        script => 1, style => 1, title => 1,                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3694                       }->{$token->{tag_name}}) {                  !!!cp ('t104');
3695                !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3696                $self->{insertion_mode} = 'in head';                  push @{$self->{open_elements}},
3697                ## reprocess                      [$self->{head_element}, $el_category->{head}];
3698                redo B;                } else {
3699              } else {                  !!!cp ('t105');
               #  
             }  
           } else {  
             #  
           }  
             
           ## As if <body>  
           !!!insert-element ('body');  
           $self->{insertion_mode} = 'in body';  
           ## reprocess  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There is a code clone of "character in body".  
             $reconstruct_active_formatting_elements->($insert_to_current);  
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## NOTE: There is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
3700                }                }
3701              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3702                  my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3703    
3704              !!!parse-error (type => 'in table:#character');                unless ($self->{confident}) {
3705                    if ($token->{attributes}->{charset}) { ## TODO: And if supported
3706              ## As if in body, but insert into foster parent element                    !!!cp ('t106');
3707              ## ISSUE: Spec says that "whenever a node would be inserted                    $self->{change_encoding}
3708              ## into the current node" while characters might not be                        ->($self, $token->{attributes}->{charset}->{value},
3709              ## result in a new Text node.                           $token);
3710              $reconstruct_active_formatting_elements->($insert_to_foster);                    
3711                                  $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3712              if ({                        ->set_user_data (manakai_has_reference =>
3713                   table => 1, tbody => 1, tfoot => 1,                                             $token->{attributes}->{charset}
3714                   thead => 1, tr => 1,                                                 ->{has_reference});
3715                  }->{$self->{open_elements}->[-1]->[1]}) {                  } elsif ($token->{attributes}->{content}) {
3716                # MUST                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
3717                my $foster_parent_element;                    if ($token->{attributes}->{content}->{value}
3718                my $next_sibling;                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
3719                my $prev_sibling;                            [\x09-\x0D\x20]*=
3720                OE: for (reverse 0..$#{$self->{open_elements}}) {                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
3721                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
3722                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                      !!!cp ('t107');
3723                    if (defined $parent and $parent->node_type == 1) {                      $self->{change_encoding}
3724                      $foster_parent_element = $parent;                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
3725                      $next_sibling = $self->{open_elements}->[$_]->[0];                             $token);
3726                      $prev_sibling = $next_sibling->previous_sibling;                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3727                            ->set_user_data (manakai_has_reference =>
3728                                                 $token->{attributes}->{content}
3729                                                       ->{has_reference});
3730                    } else {                    } else {
3731                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      !!!cp ('t108');
                     $prev_sibling = $foster_parent_element->last_child;  
3732                    }                    }
                   last OE;  
3733                  }                  }
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
3734                } else {                } else {
3735                  $foster_parent_element->insert_before                  if ($token->{attributes}->{charset}) {
3736                    ($self->{document}->create_text_node ($token->{data}),                    !!!cp ('t109');
3737                     $next_sibling);                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3738                }                        ->set_user_data (manakai_has_reference =>
3739              } else {                                             $token->{attributes}->{charset}
3740                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                                                 ->{has_reference});
3741              }                  }
3742                                if ($token->{attributes}->{content}) {
3743              !!!next-token;                    !!!cp ('t110');
3744              redo B;                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3745            } elsif ($token->{type} eq 'comment') {                        ->set_user_data (manakai_has_reference =>
3746              my $comment = $self->{document}->create_comment ($token->{data});                                             $token->{attributes}->{content}
3747              $self->{open_elements}->[-1]->[0]->append_child ($comment);                                                 ->{has_reference});
3748              !!!next-token;                  }
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  caption => 1,  
                  colgroup => 1,  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
3749                }                }
3750    
3751                push @$active_formatting_elements, ['#marker', '']                pop @{$self->{open_elements}} # <head>
3752                  if $token->{tag_name} eq 'caption';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
3753                !!!next-token;                !!!next-token;
3754                redo B;                redo B;
3755              } elsif ({              } elsif ($token->{tag_name} eq 'title') {
3756                        col => 1,                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3757                        td => 1, th => 1, tr => 1,                  !!!cp ('t111');
3758                       }->{$token->{tag_name}}) {                  ## As if </noscript>
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
3759                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3760                    !!!parse-error (type => 'in noscript:title', token => $token);
3761                  
3762                    $self->{insertion_mode} = IN_HEAD_IM;
3763                    ## Reprocess in the "in head" insertion mode...
3764                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3765                    !!!cp ('t112');
3766                    !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3767                    push @{$self->{open_elements}},
3768                        [$self->{head_element}, $el_category->{head}];
3769                  } else {
3770                    !!!cp ('t113');
3771                }                }
3772    
3773                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                ## NOTE: There is a "as if in head" code clone.
3774                $self->{insertion_mode} = $token->{tag_name} eq 'col'                my $parent = defined $self->{head_element} ? $self->{head_element}
3775                  ? 'in column group' : 'in table body';                    : $self->{open_elements}->[-1]->[0];
3776                ## reprocess                $parse_rcdata->(RCDATA_CONTENT_MODEL);
3777                  pop @{$self->{open_elements}} # <head>
3778                      if $self->{insertion_mode} == AFTER_HEAD_IM;
3779                redo B;                redo B;
3780              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'style') {
3781                ## NOTE: There are code clones for this "table in table"                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
3782                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## insertion mode IN_HEAD_IM)
3783                  ## NOTE: There is a "as if in head" code clone.
3784                ## As if </table>                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3785                ## have a table element in table scope                  !!!cp ('t114');
3786                my $i;                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3787                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  push @{$self->{open_elements}},
3788                  my $node = $self->{open_elements}->[$_];                      [$self->{head_element}, $el_category->{head}];
3789                  if ($node->[1] eq 'table') {                } else {
3790                    $i = $_;                  !!!cp ('t115');
3791                    last INSCOPE;                }
3792                  } elsif ({                $parse_rcdata->(CDATA_CONTENT_MODEL);
3793                            table => 1, html => 1,                pop @{$self->{open_elements}} # <head>
3794                           }->{$node->[1]}) {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3795                    last INSCOPE;                redo B;
3796                  }              } elsif ($token->{tag_name} eq 'noscript') {
3797                } # INSCOPE                if ($self->{insertion_mode} == IN_HEAD_IM) {
3798                unless (defined $i) {                  !!!cp ('t116');
3799                  !!!parse-error (type => 'unmatched end tag:table');                  ## NOTE: and scripting is disalbed
3800                  ## Ignore tokens </table><table>                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3801                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
3802                  !!!next-token;                  !!!next-token;
3803                  redo B;                  redo B;
3804                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3805                    !!!cp ('t117');
3806                    !!!parse-error (type => 'in noscript:noscript', token => $token);
3807                    ## Ignore the token
3808                    !!!next-token;
3809                    redo B;
3810                  } else {
3811                    !!!cp ('t118');
3812                    #
3813                }                }
3814                } elsif ($token->{tag_name} eq 'script') {
3815                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3816                    !!!cp ('t119');
3817                    ## As if </noscript>
3818                    pop @{$self->{open_elements}};
3819                    !!!parse-error (type => 'in noscript:script', token => $token);
3820                                
3821                ## generate implied end tags                  $self->{insertion_mode} = IN_HEAD_IM;
3822                if ({                  ## Reprocess in the "in head" insertion mode...
3823                     dd => 1, dt => 1, li => 1, p => 1,                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3824                     td => 1, th => 1, tr => 1,                  !!!cp ('t120');
3825                    }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3826                  !!!back-token; # <table>                  push @{$self->{open_elements}},
3827                  $token = {type => 'end tag', tag_name => 'table'};                      [$self->{head_element}, $el_category->{head}];
3828                  !!!back-token;                } else {
3829                  $token = {type => 'end tag',                  !!!cp ('t121');
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
3830                }                }
3831    
3832                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## NOTE: There is a "as if in head" code clone.
3833                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                $script_start_tag->();
3834                  pop @{$self->{open_elements}} # <head>
3835                      if $self->{insertion_mode} == AFTER_HEAD_IM;
3836                  redo B;
3837                } elsif ($token->{tag_name} eq 'body' or
3838                         $token->{tag_name} eq 'frameset') {
3839                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3840                    !!!cp ('t122');
3841                    ## As if </noscript>
3842                    pop @{$self->{open_elements}};
3843                    !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);
3844                    
3845                    ## Reprocess in the "in head" insertion mode...
3846                    ## As if </head>
3847                    pop @{$self->{open_elements}};
3848                    
3849                    ## Reprocess in the "after head" insertion mode...
3850                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3851                    !!!cp ('t124');
3852                    pop @{$self->{open_elements}};
3853                    
3854                    ## Reprocess in the "after head" insertion mode...
3855                  } else {
3856                    !!!cp ('t125');
3857                }                }
3858    
3859                splice @{$self->{open_elements}}, $i;                ## "after head" insertion mode
3860                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3861                $self->_reset_insertion_mode;                if ($token->{tag_name} eq 'body') {
3862                    !!!cp ('t126');
3863                ## reprocess                  $self->{insertion_mode} = IN_BODY_IM;
3864                  } elsif ($token->{tag_name} eq 'frameset') {
3865                    !!!cp ('t127');
3866                    $self->{insertion_mode} = IN_FRAMESET_IM;
3867                  } else {
3868                    die "$0: tag name: $self->{tag_name}";
3869                  }
3870                  !!!next-token;
3871                redo B;                redo B;
3872              } else {              } else {
3873                  !!!cp ('t128');
3874                #                #
3875              }              }
3876            } elsif ($token->{type} eq 'end tag') {  
3877              if ($token->{tag_name} eq 'table') {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3878                ## have a table element in table scope                !!!cp ('t129');
3879                my $i;                ## As if </noscript>
3880                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                pop @{$self->{open_elements}};
3881                  my $node = $self->{open_elements}->[$_];                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
3882                  if ($node->[1] eq $token->{tag_name}) {                
3883                    $i = $_;                ## Reprocess in the "in head" insertion mode...
3884                    last INSCOPE;                ## As if </head>
3885                  } elsif ({                pop @{$self->{open_elements}};
3886                            table => 1, html => 1,  
3887                           }->{$node->[1]}) {                ## Reprocess in the "after head" insertion mode...
3888                    last INSCOPE;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3889                  }                !!!cp ('t130');
3890                } # INSCOPE                ## As if </head>
3891                unless (defined $i) {                pop @{$self->{open_elements}};
3892                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
3893                  ## Ignore the token                ## Reprocess in the "after head" insertion mode...
3894                } else {
3895                  !!!cp ('t131');
3896                }
3897    
3898                ## "after head" insertion mode
3899                ## As if <body>
3900                !!!insert-element ('body',, $token);
3901                $self->{insertion_mode} = IN_BODY_IM;
3902                ## reprocess
3903                redo B;
3904              } elsif ($token->{type} == END_TAG_TOKEN) {
3905                if ($token->{tag_name} eq 'head') {
3906                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3907                    !!!cp ('t132');
3908                    ## As if <head>
3909                    !!!create-element ($self->{head_element}, 'head',, $token);
3910                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3911                    push @{$self->{open_elements}},
3912                        [$self->{head_element}, $el_category->{head}];
3913    
3914                    ## Reprocess in the "in head" insertion mode...
3915                    pop @{$self->{open_elements}};
3916                    $self->{insertion_mode} = AFTER_HEAD_IM;
3917                    !!!next-token;
3918                    redo B;
3919                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3920                    !!!cp ('t133');
3921                    ## As if </noscript>
3922                    pop @{$self->{open_elements}};
3923                    !!!parse-error (type => 'in noscript:/head', token => $token);
3924                    
3925                    ## Reprocess in the "in head" insertion mode...
3926                    pop @{$self->{open_elements}};
3927                    $self->{insertion_mode} = AFTER_HEAD_IM;
3928                    !!!next-token;
3929                    redo B;
3930                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3931                    !!!cp ('t134');
3932                    pop @{$self->{open_elements}};
3933                    $self->{insertion_mode} = AFTER_HEAD_IM;
3934                    !!!next-token;
3935                    redo B;
3936                  } else {
3937                    !!!cp ('t135');
3938                    #
3939                  }
3940                } elsif ($token->{tag_name} eq 'noscript') {
3941                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3942                    !!!cp ('t136');
3943                    pop @{$self->{open_elements}};
3944                    $self->{insertion_mode} = IN_HEAD_IM;
3945                    !!!next-token;
3946                    redo B;
3947                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3948                    !!!cp ('t137');
3949                    !!!parse-error (type => 'unmatched end tag:noscript', token => $token);
3950                    ## Ignore the token ## ISSUE: An issue in the spec.
3951                  !!!next-token;                  !!!next-token;
3952                  redo B;                  redo B;
3953                  } else {
3954                    !!!cp ('t138');
3955                    #
3956                }                }
3957                              } elsif ({
3958                ## generate implied end tags                        body => 1, html => 1,
3959                if ({                       }->{$token->{tag_name}}) {
3960                     dd => 1, dt => 1, li => 1, p => 1,                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3961                     td => 1, th => 1, tr => 1,                  !!!cp ('t139');
3962                    }->{$self->{open_elements}->[-1]->[1]}) {                  ## As if <head>
3963                  !!!back-token;                  !!!create-element ($self->{head_element}, 'head',, $token);
3964                  $token = {type => 'end tag',                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3965                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                  push @{$self->{open_elements}},
3966                        [$self->{head_element}, $el_category->{head}];
3967    
3968                    $self->{insertion_mode} = IN_HEAD_IM;
3969                    ## Reprocess in the "in head" insertion mode...
3970                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3971                    !!!cp ('t140');
3972                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
3973                    ## Ignore the token
3974                    !!!next-token;
3975                  redo B;                  redo B;
3976                  } else {
3977                    !!!cp ('t141');
3978                }                }
3979                  
3980                  #
3981                } elsif ({
3982                          p => 1, br => 1,
3983                         }->{$token->{tag_name}}) {
3984                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3985                    !!!cp ('t142');
3986                    ## As if <head>
3987                    !!!create-element ($self->{head_element}, 'head',, $token);
3988                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3989                    push @{$self->{open_elements}},
3990                        [$self->{head_element}, $el_category->{head}];
3991    
3992                if ($self->{open_elements}->[-1]->[1] ne 'table') {                  $self->{insertion_mode} = IN_HEAD_IM;
3993                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  ## Reprocess in the "in head" insertion mode...
3994                  } else {
3995                    !!!cp ('t143');
3996                }                }
3997    
3998                splice @{$self->{open_elements}}, $i;                #
3999                } else {
4000                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4001                    !!!cp ('t144');
4002                    #
4003                  } else {
4004                    !!!cp ('t145');
4005                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4006                    ## Ignore the token
4007                    !!!next-token;
4008                    redo B;
4009                  }
4010                }
4011    
4012                $self->_reset_insertion_mode;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4013                  !!!cp ('t146');
4014                  ## As if </noscript>
4015                  pop @{$self->{open_elements}};
4016                  !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
4017                  
4018                  ## Reprocess in the "in head" insertion mode...
4019                  ## As if </head>
4020                  pop @{$self->{open_elements}};
4021    
4022                !!!next-token;                ## Reprocess in the "after head" insertion mode...
4023                redo B;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4024              } elsif ({                !!!cp ('t147');
4025                        body => 1, caption => 1, col => 1, colgroup => 1,                ## As if </head>
4026                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,                pop @{$self->{open_elements}};
4027                        thead => 1, tr => 1,  
4028                       }->{$token->{tag_name}}) {                ## Reprocess in the "after head" insertion mode...
4029                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4030                ## Ignore the token  ## ISSUE: This case cannot be reached?
4031                  !!!cp ('t148');
4032                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4033                  ## Ignore the token ## ISSUE: An issue in the spec.
4034                !!!next-token;                !!!next-token;
4035                redo B;                redo B;
4036              } else {              } else {
4037                #                !!!cp ('t149');
4038              }              }
           } else {  
             #  
           }  
4039    
4040            !!!parse-error (type => 'in table:'.$token->{tag_name});              ## "after head" insertion mode
4041            $in_body->($insert_to_foster);              ## As if <body>
4042            redo B;              !!!insert-element ('body',, $token);
4043          } elsif ($self->{insertion_mode} eq 'in caption') {              $self->{insertion_mode} = IN_BODY_IM;
4044            if ($token->{type} eq 'character') {              ## reprocess
4045              ## NOTE: This is a code clone of "character in body".              redo B;
4046          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4047            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4048              !!!cp ('t149.1');
4049    
4050              ## NOTE: As if <head>
4051              !!!create-element ($self->{head_element}, 'head',, $token);
4052              $self->{open_elements}->[-1]->[0]->append_child
4053                  ($self->{head_element});
4054              #push @{$self->{open_elements}},
4055              #    [$self->{head_element}, $el_category->{head}];
4056              #$self->{insertion_mode} = IN_HEAD_IM;
4057              ## NOTE: Reprocess.
4058    
4059              ## NOTE: As if </head>
4060              #pop @{$self->{open_elements}};
4061              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4062              ## NOTE: Reprocess.
4063              
4064              #
4065            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4066              !!!cp ('t149.2');
4067    
4068              ## NOTE: As if </head>
4069              pop @{$self->{open_elements}};
4070              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4071              ## NOTE: Reprocess.
4072    
4073              #
4074            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4075              !!!cp ('t149.3');
4076    
4077              !!!parse-error (type => 'in noscript:#eof', token => $token);
4078    
4079              ## As if </noscript>
4080              pop @{$self->{open_elements}};
4081              #$self->{insertion_mode} = IN_HEAD_IM;
4082              ## NOTE: Reprocess.
4083    
4084              ## NOTE: As if </head>
4085              pop @{$self->{open_elements}};
4086              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4087              ## NOTE: Reprocess.
4088    
4089              #
4090            } else {
4091              !!!cp ('t149.4');
4092              #
4093            }
4094    
4095            ## NOTE: As if <body>
4096            !!!insert-element ('body',, $token);
4097            $self->{insertion_mode} = IN_BODY_IM;
4098            ## NOTE: Reprocess.
4099            redo B;
4100          } else {
4101            die "$0: $token->{type}: Unknown token type";
4102          }
4103    
4104              ## ISSUE: An issue in the spec.
4105        } elsif ($self->{insertion_mode} & BODY_IMS) {
4106              if ($token->{type} == CHARACTER_TOKEN) {
4107                !!!cp ('t150');
4108                ## NOTE: There is a code clone of "character in body".
4109              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
4110                            
4111              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4112    
4113              !!!next-token;              !!!next-token;
4114              redo B;              redo B;
4115            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} == START_TAG_TOKEN) {
             ## NOTE: This is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
4116              if ({              if ({
4117                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
4118                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
4119                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4120                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
4121                    ## have an element in table scope
4122                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
4123                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
4124                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
4125                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
4126                  my $node = $self->{open_elements}->[$_];  
4127                  if ($node->[1] eq 'caption') {                      ## Close the cell
4128                    $i = $_;                      !!!back-token; # <?>
4129                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
4130                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
4131                            table => 1, html => 1,                                line => $token->{line},
4132                           }->{$node->[1]}) {                                column => $token->{column}};
4133                    last INSCOPE;                      redo B;
4134                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4135                        !!!cp ('t152');
4136                        ## ISSUE: This case can never be reached, maybe.
4137                        last;
4138                      }
4139                  }                  }
4140                } # INSCOPE  
4141                unless (defined $i) {                  !!!cp ('t153');
4142                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
4143                        value => $token->{tag_name}, token => $token);
4144                  ## Ignore the token                  ## Ignore the token
4145                  !!!next-token;                  !!!next-token;
4146                  redo B;                  redo B;
4147                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4148                                  !!!parse-error (type => 'not closed:caption', token => $token);
4149                ## generate implied end tags                  
4150                if ({                  ## NOTE: As if </caption>.
4151                     dd => 1, dt => 1, li => 1, p => 1,                  ## have a table element in table scope
4152                     td => 1, th => 1, tr => 1,                  my $i;
4153                    }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: {
4154                  !!!back-token; # <?>                    for (reverse 0..$#{$self->{open_elements}}) {
4155                  $token = {type => 'end tag', tag_name => 'caption'};                      my $node = $self->{open_elements}->[$_];
4156                  !!!back-token;                      if ($node->[1] & CAPTION_EL) {
4157                  $token = {type => 'end tag',                        !!!cp ('t155');
4158                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        $i = $_;
4159                  redo B;                        last INSCOPE;
4160                }                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4161                          !!!cp ('t156');
4162                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        last;
4163                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      }
4164                }                    }
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
4165    
4166                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
4167                      !!!parse-error (type => 'start tag not allowed',
4168                                      value => $token->{tag_name}, token => $token);
4169                      ## Ignore the token
4170                      !!!next-token;
4171                      redo B;
4172                    } # INSCOPE
4173                    
4174                    ## generate implied end tags
4175                    while ($self->{open_elements}->[-1]->[1]
4176                               & END_TAG_OPTIONAL_EL) {
4177                      !!!cp ('t158');
4178                      pop @{$self->{open_elements}};
4179                    }
4180    
4181                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4182                redo B;                    !!!cp ('t159');
4183                      !!!parse-error (type => 'not closed',
4184                                      value => $self->{open_elements}->[-1]->[0]
4185                                          ->manakai_local_name,
4186                                      token => $token);
4187                    } else {
4188                      !!!cp ('t160');
4189                    }
4190                    
4191                    splice @{$self->{open_elements}}, $i;
4192                    
4193                    $clear_up_to_marker->();
4194                    
4195                    $self->{insertion_mode} = IN_TABLE_IM;
4196                    
4197                    ## reprocess
4198                    redo B;
4199                  } else {
4200                    !!!cp ('t161');
4201                    #
4202                  }
4203              } else {              } else {
4204                  !!!cp ('t162');
4205                #                #
4206              }              }
4207            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
4208              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
4209                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
4210                my $i;                  ## have an element in table scope
4211                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
4212                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4213                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
4214                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4215                    last INSCOPE;                      !!!cp ('t163');
4216                  } elsif ({                      $i = $_;
4217                            table => 1, html => 1,                      last INSCOPE;
4218                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4219                    last INSCOPE;                      !!!cp ('t164');
4220                        last INSCOPE;
4221                      }
4222                    } # INSCOPE
4223                      unless (defined $i) {
4224                        !!!cp ('t165');
4225                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4226                        ## Ignore the token
4227                        !!!next-token;
4228                        redo B;
4229                      }
4230                    
4231                    ## generate implied end tags
4232                    while ($self->{open_elements}->[-1]->[1]
4233                               & END_TAG_OPTIONAL_EL) {
4234                      !!!cp ('t166');
4235                      pop @{$self->{open_elements}};
4236                  }                  }
4237                } # INSCOPE  
4238                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
4239                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
4240                  ## Ignore the token                    !!!cp ('t167');
4241                      !!!parse-error (type => 'not closed',
4242                                      value => $self->{open_elements}->[-1]->[0]
4243                                          ->manakai_local_name,
4244                                      token => $token);
4245                    } else {
4246                      !!!cp ('t168');
4247                    }
4248                    
4249                    splice @{$self->{open_elements}}, $i;
4250                    
4251                    $clear_up_to_marker->();
4252                    
4253                    $self->{insertion_mode} = IN_ROW_IM;
4254                    
4255                  !!!next-token;                  !!!next-token;
4256                  redo B;                  redo B;
4257                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4258                                  !!!cp ('t169');
4259                ## generate implied end tags                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4260                if ({                  ## Ignore the token
4261                     dd => 1, dt => 1, li => 1, p => 1,                  !!!next-token;
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
4262                  redo B;                  redo B;
4263                  } else {
4264                    !!!cp ('t170');
4265                    #
4266                }                }
4267                } elsif ($token->{tag_name} eq 'caption') {
4268                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
4269                    ## have a table element in table scope
4270                    my $i;
4271                    INSCOPE: {
4272                      for (reverse 0..$#{$self->{open_elements}}) {
4273                        my $node = $self->{open_elements}->[$_];
4274                        if ($node->[1] & CAPTION_EL) {
4275                          !!!cp ('t171');
4276                          $i = $_;
4277                          last INSCOPE;
4278                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
4279                          !!!cp ('t172');
4280                          last;
4281                        }
4282                      }
4283    
4284                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
4285                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
4286                                      value => $token->{tag_name}, token => $token);
4287                      ## Ignore the token
4288                      !!!next-token;
4289                      redo B;
4290                    } # INSCOPE
4291                    
4292                    ## generate implied end tags
4293                    while ($self->{open_elements}->[-1]->[1]
4294                               & END_TAG_OPTIONAL_EL) {
4295                      !!!cp ('t174');
4296                      pop @{$self->{open_elements}};
4297                    }
4298                    
4299                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4300                      !!!cp ('t175');
4301                      !!!parse-error (type => 'not closed',
4302                                      value => $self->{open_elements}->[-1]->[0]
4303                                          ->manakai_local_name,
4304                                      token => $token);
4305                    } else {
4306                      !!!cp ('t176');
4307                    }
4308                    
4309                    splice @{$self->{open_elements}}, $i;
4310                    
4311                    $clear_up_to_marker->();
4312                    
4313                    $self->{insertion_mode} = IN_TABLE_IM;
4314                    
4315                    !!!next-token;
4316                    redo B;
4317                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
4318                    !!!cp ('t177');
4319                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4320                    ## Ignore the token
4321                    !!!next-token;
4322                    redo B;
4323                  } else {
4324                    !!!cp ('t178');
4325                    #
4326                }                }
4327                } elsif ({
4328                          table => 1, tbody => 1, tfoot => 1,
4329                          thead => 1, tr => 1,
4330                         }->{$token->{tag_name}} and
4331                         $self->{insertion_mode} == IN_CELL_IM) {
4332                  ## have an element in table scope
4333                  my $i;
4334                  my $tn;
4335                  INSCOPE: {
4336                    for (reverse 0..$#{$self->{open_elements}}) {
4337                      my $node = $self->{open_elements}->[$_];
4338                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4339                        !!!cp ('t179');
4340                        $i = $_;
4341    
4342                        ## Close the cell
4343                        !!!back-token; # </?>
4344                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
4345                                  line => $token->{line},
4346                                  column => $token->{column}};
4347                        redo B;
4348                      } elsif ($node->[1] & TABLE_CELL_EL) {
4349                        !!!cp ('t180');
4350                        $tn = $node->[0]->manakai_local_name;
4351                        ## NOTE: There is exactly one |td| or |th| element
4352                        ## in scope in the stack of open elements by definition.
4353                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4354                        ## ISSUE: Can this be reached?
4355                        !!!cp ('t181');
4356                        last;
4357                      }
4358                    }
4359    
4360                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
4361                    !!!parse-error (type => 'unmatched end tag',
4362                $clear_up_to_marker->();                      value => $token->{tag_name}, token => $token);
4363                    ## Ignore the token
4364                $self->{insertion_mode} = 'in table';                  !!!next-token;
4365                    redo B;
4366                !!!next-token;                } # INSCOPE
4367                redo B;              } elsif ($token->{tag_name} eq 'table' and
4368              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
4369                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed:caption', token => $token);
4370    
4371                ## As if </caption>                ## As if </caption>
4372                ## have a table element in table scope                ## have a table element in table scope
4373                my $i;                my $i;
4374                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4375                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4376                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
4377                      !!!cp ('t184');
4378                    $i = $_;                    $i = $_;
4379                    last INSCOPE;                    last INSCOPE;
4380                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4381                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
4382                    last INSCOPE;                    last INSCOPE;
4383                  }                  }
4384                } # INSCOPE                } # INSCOPE
4385                unless (defined $i) {                unless (defined $i) {
4386                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
4387                    !!!parse-error (type => 'unmatched end tag:caption', token => $token);
4388                  ## Ignore the token                  ## Ignore the token
4389                  !!!next-token;                  !!!next-token;
4390                  redo B;                  redo B;
4391                }                }
4392                                
4393                ## generate implied end tags                ## generate implied end tags
4394                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4395                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
4396                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
4397                }                }
4398    
4399                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4400                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
4401                    !!!parse-error (type => 'not closed',
4402                                    value => $self->{open_elements}->[-1]->[0]
4403                                        ->manakai_local_name,
4404                                    token => $token);
4405                  } else {
4406                    !!!cp ('t189');
4407                }                }
4408    
4409                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4410    
4411                $clear_up_to_marker->();                $clear_up_to_marker->();
4412    
4413                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
4414    
4415                ## reprocess                ## reprocess
4416                redo B;                redo B;
4417              } elsif ({              } elsif ({
4418                        body => 1, col => 1, colgroup => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
                       html => 1, tbody => 1, td => 1, tfoot => 1,  
                       th => 1, thead => 1, tr => 1,  
4419                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4420                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
4421                ## Ignore the token                  !!!cp ('t190');
4422                redo B;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
             } else {  
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'col') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html') {  
                 !!!parse-error (type => 'unmatched end tag:colgroup');  
4423                  ## Ignore the token                  ## Ignore the token
4424                  !!!next-token;                  !!!next-token;
4425                  redo B;                  redo B;
4426                } else {                } else {
4427                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
4428                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
4429                }                }
4430              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
4431                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
4432                          thead => 1, tr => 1,
4433                         }->{$token->{tag_name}} and
4434                         $self->{insertion_mode} == IN_CAPTION_IM) {
4435                  !!!cp ('t192');
4436                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4437                ## Ignore the token                ## Ignore the token
4438                !!!next-token;                !!!next-token;
4439                redo B;                redo B;
4440              } else {              } else {
4441                #                !!!cp ('t193');
4442                  #
4443              }              }
4444            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4445              #          for my $entry (@{$self->{open_elements}}) {
4446              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
4447                !!!cp ('t75');
4448                !!!parse-error (type => 'in body:#eof', token => $token);
4449                last;
4450            }            }
4451            }
4452    
4453            ## As if </colgroup>          ## Stop parsing.
4454            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
4455              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
4456              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
4457          }
4458    
4459          $insert = $insert_to_current;
4460          #
4461        } elsif ($self->{insertion_mode} & TABLE_IMS) {
4462          if ($token->{type} == CHARACTER_TOKEN) {
4463            if (not $open_tables->[-1]->[1] and # tainted
4464                $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4465              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4466                  
4467              unless (length $token->{data}) {
4468                !!!cp ('t194');
4469              !!!next-token;              !!!next-token;
4470              redo B;              redo B;
4471            } else {            } else {
4472              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
4473            }            }
4474          } elsif ($self->{insertion_mode} eq 'in table body') {          }
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
4475    
4476              !!!parse-error (type => 'in table:#character');              !!!parse-error (type => 'in table:#character', token => $token);
4477    
4478              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
4479              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
4480              ## into the current node" while characters might not be              ## into the current node" while characters might not be
4481              ## result in a new Text node.              ## result in a new Text node.
4482              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
4483                
4484              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
4485                # MUST                # MUST
4486                my $foster_parent_element;                my $foster_parent_element;
4487                my $next_sibling;                my $next_sibling;
4488                my $prev_sibling;                my $prev_sibling;
4489                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
4490                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4491                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4492                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
4493                        !!!cp ('t196');
4494                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
4495                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
4496                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
4497                    } else {                    } else {
4498                        !!!cp ('t197');
4499                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
4500                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
4501                    }                    }
# Line 3674  sub _tree_construction_main ($) { Line 4507  sub _tree_construction_main ($) {
4507                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
4508                if (defined $prev_sibling and                if (defined $prev_sibling and
4509                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
4510                    !!!cp ('t198');
4511                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
4512                } else {                } else {
4513                    !!!cp ('t199');
4514                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
4515                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
4516                     $next_sibling);                     $next_sibling);
4517                }                }
4518              } else {            $open_tables->[-1]->[1] = 1; # tainted
4519                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
4520              }            !!!cp ('t200');
4521              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4522            }
4523                            
4524              !!!next-token;          !!!next-token;
4525              redo B;          redo B;
4526            } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == START_TAG_TOKEN) {
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
4527              if ({              if ({
4528                   tr => 1,                   tr => ($self->{insertion_mode} != IN_ROW_IM),
4529                   th => 1, td => 1,                   th => 1, td => 1,
4530                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4531                unless ($token->{tag_name} eq 'tr') {                if ($self->{insertion_mode} == IN_TABLE_IM) {
4532                  !!!parse-error (type => 'missing start tag:tr');                  ## Clear back to table context
4533                    while (not ($self->{open_elements}->[-1]->[1]
4534                                    & TABLE_SCOPING_EL)) {
4535                      !!!cp ('t201');
4536                      pop @{$self->{open_elements}};
4537                    }
4538                    
4539                    !!!insert-element ('tbody',, $token);
4540                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
4541                    ## reprocess in the "in table body" insertion mode...
4542                }                }
4543    
4544                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4545                while (not {                  unless ($token->{tag_name} eq 'tr') {
4546                  tbody => 1, tfoot => 1, thead => 1, html => 1,                    !!!cp ('t202');
4547                }->{$self->{open_elements}->[-1]->[1]}) {                    !!!parse-error (type => 'missing start tag:tr', token => $token);
4548                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  }
4549                    
4550                    ## Clear back to table body context
4551                    while (not ($self->{open_elements}->[-1]->[1]
4552                                    & TABLE_ROWS_SCOPING_EL)) {
4553                      !!!cp ('t203');
4554                      ## ISSUE: Can this case be reached?
4555                      pop @{$self->{open_elements}};
4556                    }
4557                    
4558                    $self->{insertion_mode} = IN_ROW_IM;
4559                    if ($token->{tag_name} eq 'tr') {
4560                      !!!cp ('t204');
4561                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4562                      !!!next-token;
4563                      redo B;
4564                    } else {
4565                      !!!cp ('t205');
4566                      !!!insert-element ('tr',, $token);
4567                      ## reprocess in the "in row" insertion mode
4568                    }
4569                  } else {
4570                    !!!cp ('t206');
4571                  }
4572    
4573                  ## Clear back to table row context
4574                  while (not ($self->{open_elements}->[-1]->[1]
4575                                  & TABLE_ROW_SCOPING_EL)) {
4576                    !!!cp ('t207');
4577                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4578                }                }
4579                                
4580                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4581                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
4582                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
4583                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
4584                } else {                
4585                  !!!insert-element ('tr');                !!!next-token;
                 ## reprocess  
               }  
4586                redo B;                redo B;
4587              } elsif ({              } elsif ({
4588                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
4589                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
4590                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
4591                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4592                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
4593                my $i;                  ## As if </tr>
4594                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
4595                  my $node = $self->{open_elements}->[$_];                  my $i;
4596                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4597                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
4598                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
4599                    $i = $_;                      !!!cp ('t208');
4600                    last INSCOPE;                      $i = $_;
4601                  } elsif ({                      last INSCOPE;
4602                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4603                           }->{$node->[1]}) {                      !!!cp ('t209');
4604                    last INSCOPE;                      last INSCOPE;
4605                      }
4606                    } # INSCOPE
4607                    unless (defined $i) {
4608                     !!!cp ('t210');
4609    ## TODO: This type is wrong.
4610                     !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);
4611                      ## Ignore the token
4612                      !!!next-token;
4613                      redo B;
4614                    }
4615                    
4616                    ## Clear back to table row context
4617                    while (not ($self->{open_elements}->[-1]->[1]
4618                                    & TABLE_ROW_SCOPING_EL)) {
4619                      !!!cp ('t211');
4620                      ## ISSUE: Can this case be reached?
4621                      pop @{$self->{open_elements}};
4622                    }
4623                    
4624                    pop @{$self->{open_elements}}; # tr
4625                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
4626                    if ($token->{tag_name} eq 'tr') {
4627                      !!!cp ('t212');
4628                      ## reprocess
4629                      redo B;
4630                    } else {
4631                      !!!cp ('t213');
4632                      ## reprocess in the "in table body" insertion mode...
4633                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
4634                }                }
4635    
4636                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4637                while (not {                  ## have an element in table scope
4638                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
4639                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4640                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
4641                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
4642                        !!!cp ('t214');
4643                        $i = $_;
4644                        last INSCOPE;
4645                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4646                        !!!cp ('t215');
4647                        last INSCOPE;
4648                      }
4649                    } # INSCOPE
4650                    unless (defined $i) {
4651                      !!!cp ('t216');
4652    ## TODO: This erorr type ios wrong.
4653                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4654                      ## Ignore the token
4655                      !!!next-token;
4656                      redo B;
4657                    }
4658    
4659                    ## Clear back to table body context
4660                    while (not ($self->{open_elements}->[-1]->[1]
4661                                    & TABLE_ROWS_SCOPING_EL)) {
4662                      !!!cp ('t217');
4663                      ## ISSUE: Can this state be reached?
4664                      pop @{$self->{open_elements}};
4665                    }
4666                    
4667                    ## As if <{current node}>
4668                    ## have an element in table scope
4669                    ## true by definition
4670                    
4671                    ## Clear back to table body context
4672                    ## nop by definition
4673                    
4674                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4675                    $self->{insertion_mode} = IN_TABLE_IM;
4676                    ## reprocess in "in table" insertion mode...
4677                  } else {
4678                    !!!cp ('t218');
4679                }                }
4680    
4681                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
4682                ## have an element in table scope                  ## Clear back to table context
4683                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
4684                                    & TABLE_SCOPING_EL)) {
4685                ## Clear back to table body context                    !!!cp ('t219');
4686                ## nop by definition                    ## ISSUE: Can this state be reached?
4687                      pop @{$self->{open_elements}};
4688                pop @{$self->{open_elements}};                  }
4689                $self->{insertion_mode} = 'in table';                  
4690                ## reprocess                  !!!insert-element ('colgroup',, $token);
4691                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
4692                    ## reprocess
4693                    redo B;
4694                  } elsif ({
4695                            caption => 1,
4696                            colgroup => 1,
4697                            tbody => 1, tfoot => 1, thead => 1,
4698                           }->{$token->{tag_name}}) {
4699                    ## Clear back to table context
4700                    while (not ($self->{open_elements}->[-1]->[1]
4701                                    & TABLE_SCOPING_EL)) {
4702                      !!!cp ('t220');
4703                      ## ISSUE: Can this state be reached?
4704                      pop @{$self->{open_elements}};
4705                    }
4706                    
4707                    push @$active_formatting_elements, ['#marker', '']
4708                        if $token->{tag_name} eq 'caption';
4709                    
4710                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4711                    $self->{insertion_mode} = {
4712                                               caption => IN_CAPTION_IM,
4713                                               colgroup => IN_COLUMN_GROUP_IM,
4714                                               tbody => IN_TABLE_BODY_IM,
4715                                               tfoot => IN_TABLE_BODY_IM,
4716                                               thead => IN_TABLE_BODY_IM,
4717                                              }->{$token->{tag_name}};
4718                    !!!next-token;
4719                    redo B;
4720                  } else {
4721                    die "$0: in table: <>: $token->{tag_name}";
4722                  }
4723              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4724                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
4725                !!!parse-error (type => 'not closed:table');                                value => $self->{open_elements}->[-1]->[0]
4726                                      ->manakai_local_name,
4727                                  token => $token);
4728    
4729                ## As if </table>                ## As if </table>
4730                ## have a table element in table scope                ## have a table element in table scope
4731                my $i;                my $i;
4732                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4733                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4734                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
4735                      !!!cp ('t221');
4736                    $i = $_;                    $i = $_;
4737                    last INSCOPE;                    last INSCOPE;
4738                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4739                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
4740                    last INSCOPE;                    last INSCOPE;
4741                  }                  }
4742                } # INSCOPE                } # INSCOPE
4743                unless (defined $i) {                unless (defined $i) {
4744                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
4745    ## TODO: The following is wrong, maybe.
4746                    !!!parse-error (type => 'unmatched end tag:table', token => $token);
4747                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
4748                  !!!next-token;                  !!!next-token;
4749                  redo B;                  redo B;
4750                }                }
4751                                
4752    ## TODO: Followings are removed from the latest spec.
4753                ## generate implied end tags                ## generate implied end tags
4754                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4755                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
4756                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
4757                }                }
4758    
4759                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
4760                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
4761                    ## NOTE: |<table><tr><table>|
4762                    !!!parse-error (type => 'not closed',
4763                                    value => $self->{open_elements}->[-1]->[0]
4764                                        ->manakai_local_name,
4765                                    token => $token);
4766                  } else {
4767                    !!!cp ('t226');
4768                }                }
4769    
4770                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4771                  pop @{$open_tables};
4772    
4773                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
4774    
4775                ## reprocess                ## reprocess
4776                redo B;                redo B;
4777              } else {          } elsif ($token->{tag_name} eq 'style') {
4778                #            if (not $open_tables->[-1]->[1]) { # tainted
4779              }              !!!cp ('t227.8');
4780            } elsif ($token->{type} eq 'end tag') {              ## NOTE: This is a "as if in head" code clone.
4781              if ({              $parse_rcdata->(CDATA_CONTENT_MODEL);
4782                   tbody => 1, tfoot => 1, thead => 1,              redo B;
4783                  }->{$token->{tag_name}}) {            } else {
4784                ## have an element in table scope              !!!cp ('t227.7');
4785                my $i;              #
4786                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            }
4787                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'script') {
4788                  if ($node->[1] eq $token->{tag_name}) {            if (not $open_tables->[-1]->[1]) { # tainted
4789                    $i = $_;              !!!cp ('t227.6');
4790                    last INSCOPE;              ## NOTE: This is a "as if in head" code clone.
4791                  } elsif ({              $script_start_tag->();
4792                            table => 1, html => 1,              redo B;
4793                           }->{$node->[1]}) {            } else {
4794                    last INSCOPE;              !!!cp ('t227.5');
4795                  }              #
4796                } # INSCOPE            }
4797                unless (defined $i) {          } elsif ($token->{tag_name} eq 'input') {
4798                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            if (not $open_tables->[-1]->[1]) { # tainted
4799                  ## Ignore the token              if ($token->{attributes}->{type}) { ## TODO: case
4800                  !!!next-token;                my $type = lc $token->{attributes}->{type}->{value};
4801                  redo B;                if ($type eq 'hidden') {
4802                }                  !!!cp ('t227.3');
4803                    !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4804    
4805                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4806    
4807                    ## TODO: form element pointer
4808    
               ## Clear back to table body context  
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4809                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
4810    
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'in table';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ({  
                      tbody => 1, thead => 1, tfoot => 1,  
                     }->{$node->[1]}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
4811                  !!!next-token;                  !!!next-token;
4812                  redo B;                  redo B;
4813                  } else {
4814                    !!!cp ('t227.2');
4815                    #
4816                }                }
   
               ## Clear back to table body context  
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
   
               ## Clear back to table body context  
               ## nop by definition  
   
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'in table';  
               ## reprocess  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
4817              } else {              } else {
4818                  !!!cp ('t227.1');
4819                #                #
4820              }              }
4821            } else {            } else {
4822                !!!cp ('t227.4');
4823              #              #
4824            }            }
4825                      } else {
4826            ## As if in table            !!!cp ('t227');
4827            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
4828            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
4829    
4830              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4831    
4832              ## As if in body, but insert into foster parent element          $insert = $insert_to_foster;
4833              ## ISSUE: Spec says that "whenever a node would be inserted          #
4834              ## into the current node" while characters might not be        } elsif ($token->{type} == END_TAG_TOKEN) {
4835              ## result in a new Text node.              if ($token->{tag_name} eq 'tr' and
4836              $reconstruct_active_formatting_elements->($insert_to_foster);                  $self->{insertion_mode} == IN_ROW_IM) {
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
   
               push @$active_formatting_elements, ['#marker', ''];  
                 
               !!!next-token;  
               redo B;  
             } elsif ({  
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
4837                ## have an element in table scope                ## have an element in table scope
4838                my $i;                my $i;
4839                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4840                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4841                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
4842                      !!!cp ('t228');
4843                    $i = $_;                    $i = $_;
4844                    last INSCOPE;                    last INSCOPE;
4845                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4846                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
4847                    last INSCOPE;                    last INSCOPE;
4848                  }                  }
4849                } # INSCOPE                } # INSCOPE
4850                unless (defined $i) {                unless (defined $i) {
4851                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
4852                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4853                  ## Ignore the token                  ## Ignore the token
4854                  !!!next-token;                  !!!next-token;
4855                  redo B;                  redo B;
4856                  } else {
4857                    !!!cp ('t232');
4858                }                }
4859    
4860                ## Clear back to table row context                ## Clear back to table row context
4861                while (not {                while (not ($self->{open_elements}->[-1]->[1]
4862                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
4863                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
4864                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
4865                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4866                }                }
4867    
4868                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
4869                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
4870                ## reprocess                !!!next-token;
4871                redo B;                redo B;
4872              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4873                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
4874                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
4875                    ## have an element in table scope
4876                ## As if </table>                  my $i;
4877                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4878                my $i;                    my $node = $self->{open_elements}->[$_];
4879                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
4880                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
4881                  if ($node->[1] eq 'table') {                      $i = $_;
4882                    $i = $_;                      last INSCOPE;
4883                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4884                  } elsif ({                      !!!cp ('t234');
4885                            table => 1, html => 1,                      last INSCOPE;
4886                           }->{$node->[1]}) {                    }
4887                    last INSCOPE;                  } # INSCOPE
4888                    unless (defined $i) {
4889                      !!!cp ('t235');
4890    ## TODO: The following is wrong.
4891                      !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);
4892                      ## Ignore the token
4893                      !!!next-token;
4894                      redo B;
4895                  }                  }
4896                } # INSCOPE                  
4897                unless (defined $i) {                  ## Clear back to table row context
4898                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
4899                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
4900                  !!!next-token;                    !!!cp ('t236');
4901                  redo B;  ## ISSUE: Can this state be reached?
4902                }                    pop @{$self->{open_elements}};
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
4903                  }                  }
4904                } # INSCOPE                  
4905                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
4906                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
4907                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
4908                  !!!next-token;                }
4909                  redo B;  
4910                }                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4911                    ## have an element in table scope
4912                ## Clear back to table row context                  my $i;
4913                while (not {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4914                  tr => 1, html => 1,                    my $node = $self->{open_elements}->[$_];
4915                }->{$self->{open_elements}->[-1]->[1]}) {                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
4916                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      !!!cp ('t237');
4917                        $i = $_;
4918                        last INSCOPE;
4919                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4920                        !!!cp ('t238');
4921                        last INSCOPE;
4922                      }
4923                    } # INSCOPE
4924                    unless (defined $i) {
4925                      !!!cp ('t239');
4926                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4927                      ## Ignore the token
4928                      !!!next-token;
4929                      redo B;
4930                    }
4931                    
4932                    ## Clear back to table body context
4933                    while (not ($self->{open_elements}->[-1]->[1]
4934                                    & TABLE_ROWS_SCOPING_EL)) {
4935                      !!!cp ('t240');
4936                      pop @{$self->{open_elements}};
4937                    }
4938                    
4939                    ## As if <{current node}>
4940                    ## have an element in table scope
4941                    ## true by definition
4942                    
4943                    ## Clear back to table body context
4944                    ## nop by definition
4945                    
4946                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4947                    $self->{insertion_mode} = IN_TABLE_IM;
4948                    ## reprocess in the "in table" insertion mode...
4949                }                }
4950    
4951                pop @{$self->{open_elements}}; # tr                ## NOTE: </table> in the "in table" insertion mode.
4952                $self->{insertion_mode} = 'in table body';                ## When you edit the code fragment below, please ensure that
4953                !!!next-token;                ## the code for <table> in the "in table" insertion mode
4954                redo B;                ## is synced with it.
4955              } elsif ($token->{tag_name} eq 'table') {  
4956                ## As if </tr>                ## have a table element in table scope
               ## have an element in table scope  
4957                my $i;                my $i;
4958                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4959                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4960                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_EL) {
4961                      !!!cp ('t241');
4962                    $i = $_;                    $i = $_;
4963                    last INSCOPE;                    last INSCOPE;
4964                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4965                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
4966                    last INSCOPE;                    last INSCOPE;
4967                  }                  }
4968                } # INSCOPE                } # INSCOPE
4969                unless (defined $i) {                unless (defined $i) {
4970                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t243');
4971                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4972                  ## Ignore the token                  ## Ignore the token
4973                  !!!next-token;                  !!!next-token;
4974                  redo B;                  redo B;
4975                }                }
4976                    
4977                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
4978                while (not {                pop @{$open_tables};
4979                  tr => 1, html => 1,                
4980                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
4981                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
4982                  pop @{$self->{open_elements}};                !!!next-token;
               }  
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               ## reprocess  
4983                redo B;                redo B;
4984              } elsif ({              } elsif ({
4985                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
4986                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}} and
4987                ## have an element in table scope                       $self->{insertion_mode} & ROW_IMS) {
4988                my $i;                if ($self->{insertion_mode} == IN_ROW_IM) {
4989                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
4990                  my $node = $self->{open_elements}->[$_];                  my $i;
4991                  if ($node->[1] eq $token->{tag_name}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4992                    $i = $_;                    my $node = $self->{open_elements}->[$_];
4993                    last INSCOPE;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4994                  } elsif ({                      !!!cp ('t247');
4995                            table => 1, html => 1,                      $i = $_;
4996                           }->{$node->[1]}) {                      last INSCOPE;
4997                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4998                        !!!cp ('t248');
4999                        last INSCOPE;
5000                      }
5001                    } # INSCOPE
5002                      unless (defined $i) {
5003                        !!!cp ('t249');
5004                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5005                        ## Ignore the token
5006                        !!!next-token;
5007                        redo B;
5008                      }
5009                    
5010                    ## As if </tr>
5011                    ## have an element in table scope
5012                    my $i;
5013                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5014                      my $node = $self->{open_elements}->[$_];
5015                      if ($node->[1] & TABLE_ROW_EL) {
5016                        !!!cp ('t250');
5017                        $i = $_;
5018                        last INSCOPE;
5019                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5020                        !!!cp ('t251');
5021                        last INSCOPE;
5022                      }
5023                    } # INSCOPE
5024                      unless (defined $i) {
5025                        !!!cp ('t252');
5026                        !!!parse-error (type => 'unmatched end tag:tr', token => $token);
5027                        ## Ignore the token
5028                        !!!next-token;
5029                        redo B;
5030                      }
5031                    
5032                    ## Clear back to table row context
5033                    while (not ($self->{open_elements}->[-1]->[1]
5034                                    & TABLE_ROW_SCOPING_EL)) {
5035                      !!!cp ('t253');
5036    ## ISSUE: Can this case be reached?
5037                      pop @{$self->{open_elements}};
5038                  }                  }
5039                } # INSCOPE                  
5040                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
5041                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5042                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
                 !!!next-token;  
                 redo B;  
5043                }                }
5044    
               ## As if </tr>  
5045                ## have an element in table scope                ## have an element in table scope
5046                my $i;                my $i;
5047                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5048                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5049                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5050                      !!!cp ('t254');
5051                    $i = $_;                    $i = $_;
5052                    last INSCOPE;                    last INSCOPE;
5053                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5054                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
5055                    last INSCOPE;                    last INSCOPE;
5056                  }                  }
5057                } # INSCOPE                } # INSCOPE
5058                unless (defined $i) {                unless (defined $i) {
5059                  !!!parse-error (type => 'unmatched end tag:tr');                  !!!cp ('t256');
5060                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5061                  ## Ignore the token                  ## Ignore the token
5062                  !!!next-token;                  !!!next-token;
5063                  redo B;                  redo B;
5064                }                }
5065    
5066                ## Clear back to table row context                ## Clear back to table body context
5067                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5068                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
5069                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
5070                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
5071                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5072                }                }
5073    
5074                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
5075                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
5076                ## reprocess                !!!next-token;
5077                redo B;                redo B;
5078              } elsif ({              } elsif ({
5079                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
5080                        colgroup => 1, html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
5081                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5082                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
5083                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5084                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t258');
5085                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5086                ## Ignore the token                ## Ignore the token
5087                !!!next-token;                !!!next-token;
5088                redo B;                redo B;
5089              } else {          } else {
5090                #            !!!cp ('t259');
5091              }            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);
           } else {  
             #  
           }  
5092    
5093            ## As if in table            $insert = $insert_to_foster;
5094            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5095            $in_body->($insert_to_foster);          }
5096            redo B;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5097          } elsif ($self->{insertion_mode} eq 'in cell') {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5098            if ($token->{type} eq 'character') {                  @{$self->{open_elements}} == 1) { # redundant, maybe
5099              ## NOTE: This is a code clone of "character in body".            !!!parse-error (type => 'in body:#eof', token => $token);
5100              $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t259.1');
5101                          #
5102              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5103              !!!cp ('t259.2');
5104              #
5105            }
5106    
5107              !!!next-token;          ## Stop parsing
5108              redo B;          last B;
5109            } elsif ($token->{type} eq 'comment') {        } else {
5110              ## NOTE: This is a code clone of "comment in body".          die "$0: $token->{type}: Unknown token type";
5111              my $comment = $self->{document}->create_comment ($token->{data});        }
5112              $self->{open_elements}->[-1]->[0]->append_child ($comment);      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
5113              !!!next-token;            if ($token->{type} == CHARACTER_TOKEN) {
5114              redo B;              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5115            } elsif ($token->{type} eq 'start tag') {                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5116              if ({                unless (length $token->{data}) {
5117                   caption => 1, col => 1, colgroup => 1,                  !!!cp ('t260');
                  tbody => 1, td => 1, tfoot => 1, th => 1,  
                  thead => 1, tr => 1,  
                 }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $tn) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
5118                  !!!next-token;                  !!!next-token;
5119                  redo B;                  redo B;
5120                }                }
5121                }
5122                ## Close the cell              
5123                !!!back-token; # <?>              !!!cp ('t261');
5124                $token = {type => 'end tag', tag_name => $tn};              #
5125              } elsif ($token->{type} == START_TAG_TOKEN) {
5126                if ($token->{tag_name} eq 'col') {
5127                  !!!cp ('t262');
5128                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5129                  pop @{$self->{open_elements}};
5130                  !!!next-token;
5131                redo B;                redo B;
5132              } else {              } else {
5133                  !!!cp ('t263');
5134                #                #
5135              }              }
5136            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5137              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'colgroup') {
5138                ## have an element in table scope                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5139                my $i;                  !!!cp ('t264');
5140                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5141                  ## Ignore the token                  ## Ignore the token
5142                  !!!next-token;                  !!!next-token;
5143                  redo B;                  redo B;
5144                  } else {
5145                    !!!cp ('t265');
5146                    pop @{$self->{open_elements}}; # colgroup
5147                    $self->{insertion_mode} = IN_TABLE_IM;
5148                    !!!next-token;
5149                    redo B;            
5150                }                }
5151                              } elsif ($token->{tag_name} eq 'col') {
5152                ## generate implied end tags                !!!cp ('t266');
5153                if ({                !!!parse-error (type => 'unmatched end tag:col', token => $token);
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => ($token->{tag_name} eq 'th'),  
                    th => ($token->{tag_name} eq 'td'),  
                    tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
   
               $self->{insertion_mode} = 'in row';  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5154                ## Ignore the token                ## Ignore the token
5155                !!!next-token;                !!!next-token;
5156                redo B;                redo B;
             } elsif ({  
                       table => 1, tbody => 1, tfoot => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $i;  
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
5157              } else {              } else {
5158                #                !!!cp ('t267');
5159                  #
5160              }              }
5161            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5162              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5163            }              @{$self->{open_elements}} == 1) { # redundant, maybe
5164                        !!!cp ('t270.2');
5165            $in_body->($insert_to_current);            ## Stop parsing.
5166              last B;
5167            } else {
5168              ## NOTE: As if </colgroup>.
5169              !!!cp ('t270.1');
5170              pop @{$self->{open_elements}}; # colgroup
5171              $self->{insertion_mode} = IN_TABLE_IM;
5172              ## Reprocess.
5173            redo B;            redo B;
5174          } elsif ($self->{insertion_mode} eq 'in select') {          }
5175            if ($token->{type} eq 'character') {        } else {
5176              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          die "$0: $token->{type}: Unknown token type";
5177          }
5178    
5179              ## As if </colgroup>
5180              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5181                !!!cp ('t269');
5182    ## TODO: Wrong error type?
5183                !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5184                ## Ignore the token
5185              !!!next-token;              !!!next-token;
5186              redo B;              redo B;
5187            } elsif ($token->{type} eq 'comment') {            } else {
5188              my $comment = $self->{document}->create_comment ($token->{data});              !!!cp ('t270');
5189              $self->{open_elements}->[-1]->[0]->append_child ($comment);              pop @{$self->{open_elements}}; # colgroup
5190              !!!next-token;              $self->{insertion_mode} = IN_TABLE_IM;
5191                ## reprocess
5192              redo B;              redo B;
5193            } elsif ($token->{type} eq 'start tag') {            }
5194              if ($token->{tag_name} eq 'option') {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
5195                if ($self->{open_elements}->[-1]->[1] eq 'option') {        if ($token->{type} == CHARACTER_TOKEN) {
5196                  ## As if </option>          !!!cp ('t271');
5197                  pop @{$self->{open_elements}};          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5198                }          !!!next-token;
5199            redo B;
5200          } elsif ($token->{type} == START_TAG_TOKEN) {
5201            if ($token->{tag_name} eq 'option') {
5202              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5203                !!!cp ('t272');
5204                ## As if </option>
5205                pop @{$self->{open_elements}};
5206              } else {
5207                !!!cp ('t273');
5208              }
5209    
5210                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5211                !!!next-token;            !!!next-token;
5212                redo B;            redo B;
5213              } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
5214                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5215                  ## As if </option>              !!!cp ('t274');
5216                  pop @{$self->{open_elements}};              ## As if </option>
5217                }              pop @{$self->{open_elements}};
5218              } else {
5219                !!!cp ('t275');
5220              }
5221    
5222                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5223                  ## As if </optgroup>              !!!cp ('t276');
5224                  pop @{$self->{open_elements}};              ## As if </optgroup>
5225                }              pop @{$self->{open_elements}};
5226              } else {
5227                !!!cp ('t277');
5228              }
5229    
5230                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5231                !!!next-token;            !!!next-token;
5232                redo B;            redo B;
5233              } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select' or
5234                !!!parse-error (type => 'not closed:select');                   $token->{tag_name} eq 'input' or
5235                ## As if </select> instead                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5236                ## have an element in table scope                    {
5237                my $i;                     caption => 1, table => 1,
5238                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     tbody => 1, tfoot => 1, thead => 1,
5239                  my $node = $self->{open_elements}->[$_];                     tr => 1, td => 1, th => 1,
5240                  if ($node->[1] eq $token->{tag_name}) {                    }->{$token->{tag_name}})) {
5241                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
5242                    last INSCOPE;            !!!parse-error (type => 'not closed:select', token => $token);
5243                  } elsif ({            ## NOTE: As if the token were </select> (<select> case) or
5244                            table => 1, html => 1,            ## as if there were </select> (otherwise).
5245                           }->{$node->[1]}) {            ## have an element in table scope
5246                    last INSCOPE;            my $i;
5247                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5248                } # INSCOPE              my $node = $self->{open_elements}->[$_];
5249                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
5250                  !!!parse-error (type => 'unmatched end tag:select');                !!!cp ('t278');
5251                  ## Ignore the token                $i = $_;
5252                  !!!next-token;                last INSCOPE;
5253                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5254                }                !!!cp ('t279');
5255                  last INSCOPE;
5256                }
5257              } # INSCOPE
5258              unless (defined $i) {
5259                !!!cp ('t280');
5260                !!!parse-error (type => 'unmatched end tag:select', token => $token);
5261                ## Ignore the token
5262                !!!next-token;
5263                redo B;
5264              }
5265                                
5266                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
5267              splice @{$self->{open_elements}}, $i;
5268    
5269                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5270    
5271                !!!next-token;            if ($token->{tag_name} eq 'select') {
5272                redo B;              !!!cp ('t281.2');
5273              } else {              !!!next-token;
5274                #              redo B;
5275              } else {
5276                !!!cp ('t281.1');
5277                ## Reprocess the token.
5278                redo B;
5279              }
5280            } else {
5281              !!!cp ('t282');
5282              !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);
5283              ## Ignore the token
5284              !!!next-token;
5285              redo B;
5286            }
5287          } elsif ($token->{type} == END_TAG_TOKEN) {
5288            if ($token->{tag_name} eq 'optgroup') {
5289              if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
5290                  $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
5291                !!!cp ('t283');
5292                ## As if </option>
5293                splice @{$self->{open_elements}}, -2;
5294              } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5295                !!!cp ('t284');
5296                pop @{$self->{open_elements}};
5297              } else {
5298                !!!cp ('t285');
5299                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5300                ## Ignore the token
5301              }
5302              !!!next-token;
5303              redo B;
5304            } elsif ($token->{tag_name} eq 'option') {
5305              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5306                !!!cp ('t286');
5307                pop @{$self->{open_elements}};
5308              } else {
5309                !!!cp ('t287');
5310                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5311                ## Ignore the token
5312              }
5313              !!!next-token;
5314              redo B;
5315            } elsif ($token->{tag_name} eq 'select') {
5316              ## have an element in table scope
5317              my $i;
5318              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5319                my $node = $self->{open_elements}->[$_];
5320                if ($node->[1] & SELECT_EL) {
5321                  !!!cp ('t288');
5322                  $i = $_;
5323                  last INSCOPE;
5324                } elsif ($node->[1] & TABLE_SCOPING_EL) {
5325                  !!!cp ('t289');
5326                  last INSCOPE;
5327              }              }
5328            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
5329              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
5330                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
5331                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5332                  ## As if </option>              ## Ignore the token
5333                  splice @{$self->{open_elements}}, -2;              !!!next-token;
5334                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              redo B;
5335                  pop @{$self->{open_elements}};            }
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5336                                
5337                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
5338              splice @{$self->{open_elements}}, $i;
5339    
5340                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5341    
5342                !!!next-token;            !!!next-token;
5343                redo B;            redo B;
5344              } elsif ({          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5345                        caption => 1, table => 1, tbody => 1,                   {
5346                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    caption => 1, table => 1, tbody => 1,
5347                       }->{$token->{tag_name}}) {                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
5348                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                   }->{$token->{tag_name}}) {
5349                  ## TODO: The following is wrong?
5350                ## have an element in table scope            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5351                                
5352                ## As if </select>            ## have an element in table scope
5353                ## have an element in table scope            my $i;
5354                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5355                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
5356                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5357                  if ($node->[1] eq 'select') {                !!!cp ('t292');
5358                    $i = $_;                $i = $_;
5359                    last INSCOPE;                last INSCOPE;
5360                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5361                            table => 1, html => 1,                !!!cp ('t293');
5362                           }->{$node->[1]}) {                last INSCOPE;
5363                    last INSCOPE;              }
5364                  }            } # INSCOPE
5365                } # INSCOPE            unless (defined $i) {
5366                unless (defined $i) {              !!!cp ('t294');
5367                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
5368                  ## Ignore the </select> token              !!!next-token;
5369                  !!!next-token; ## TODO: ok?              redo B;
5370                  redo B;            }
               }  
5371                                
5372                splice @{$self->{open_elements}}, $i;            ## As if </select>
5373              ## have an element in table scope
5374                $self->_reset_insertion_mode;            undef $i;
5375              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5376                ## reprocess              my $node = $self->{open_elements}->[$_];
5377                redo B;              if ($node->[1] & SELECT_EL) {
5378              } else {                !!!cp ('t295');
5379                #                $i = $_;
5380                  last INSCOPE;
5381                } elsif ($node->[1] & TABLE_SCOPING_EL) {
5382    ## ISSUE: Can this state be reached?
5383                  !!!cp ('t296');
5384                  last INSCOPE;
5385              }              }
5386            } else {            } # INSCOPE
5387              #            unless (defined $i) {
5388                !!!cp ('t297');
5389    ## TODO: The following error type is correct?
5390                !!!parse-error (type => 'unmatched end tag:select', token => $token);
5391                ## Ignore the </select> token
5392                !!!next-token; ## TODO: ok?
5393                redo B;
5394            }            }
5395                  
5396              !!!cp ('t298');
5397              splice @{$self->{open_elements}}, $i;
5398    
5399              $self->_reset_insertion_mode;
5400    
5401            !!!parse-error (type => 'in select:'.$token->{tag_name});            ## reprocess
5402              redo B;
5403            } else {
5404              !!!cp ('t299');
5405              !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);
5406            ## Ignore the token            ## Ignore the token
5407            !!!next-token;            !!!next-token;
5408            redo B;            redo B;
5409          } elsif ($self->{insertion_mode} eq 'after body') {          }
5410            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5411              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5412                ## As if in body                  @{$self->{open_elements}} == 1) { # redundant, maybe
5413                $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t299.1');
5414              !!!parse-error (type => 'in body:#eof', token => $token);
5415            } else {
5416              !!!cp ('t299.2');
5417            }
5418    
5419            ## Stop parsing.
5420            last B;
5421          } else {
5422            die "$0: $token->{type}: Unknown token type";
5423          }
5424        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
5425          if ($token->{type} == CHARACTER_TOKEN) {
5426            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5427              my $data = $1;
5428              ## As if in body
5429              $reconstruct_active_formatting_elements->($insert_to_current);
5430                                
5431                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5432              
5433              unless (length $token->{data}) {
5434                !!!cp ('t300');
5435                !!!next-token;
5436                redo B;
5437              }
5438            }
5439            
5440            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5441              !!!cp ('t301');
5442              !!!parse-error (type => 'after html:#character', token => $token);
5443    
5444                unless (length $token->{data}) {            ## Reprocess in the "after body" insertion mode.
5445                  !!!next-token;          } else {
5446                  redo B;            !!!cp ('t302');
5447                }          }
5448              }          
5449                        ## "after body" insertion mode
5450              #          !!!parse-error (type => 'after body:#character', token => $token);
5451              !!!parse-error (type => 'after body:#'.$token->{type});  
5452            } elsif ($token->{type} eq 'comment') {          $self->{insertion_mode} = IN_BODY_IM;
5453              my $comment = $self->{document}->create_comment ($token->{data});          ## reprocess
5454              $self->{open_elements}->[0]->[0]->append_child ($comment);          redo B;
5455          } elsif ($token->{type} == START_TAG_TOKEN) {
5456            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5457              !!!cp ('t303');
5458              !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5459              
5460              ## Reprocess in the "after body" insertion mode.
5461            } else {
5462              !!!cp ('t304');
5463            }
5464    
5465            ## "after body" insertion mode
5466            !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);
5467    
5468            $self->{insertion_mode} = IN_BODY_IM;
5469            ## reprocess
5470            redo B;
5471          } elsif ($token->{type} == END_TAG_TOKEN) {
5472            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5473              !!!cp ('t305');
5474              !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5475              
5476              $self->{insertion_mode} = AFTER_BODY_IM;
5477              ## Reprocess in the "after body" insertion mode.
5478            } else {
5479              !!!cp ('t306');
5480            }
5481    
5482            ## "after body" insertion mode
5483            if ($token->{tag_name} eq 'html') {
5484              if (defined $self->{inner_html_node}) {
5485                !!!cp ('t307');
5486                !!!parse-error (type => 'unmatched end tag:html', token => $token);
5487                ## Ignore the token
5488              !!!next-token;              !!!next-token;
5489              redo B;              redo B;
           } elsif ($token->{type} eq 'start tag') {  
             !!!parse-error (type => 'after body:'.$token->{tag_name});  
             #  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               if (defined $self->{inner_html_node}) {  
                 !!!parse-error (type => 'unmatched end tag:html');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               } else {  
                 $phase = 'trailing end';  
                 !!!next-token;  
                 redo B;  
               }  
             } else {  
               !!!parse-error (type => 'after body:/'.$token->{tag_name});  
             }  
5490            } else {            } else {
5491              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t308');
5492                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
5493                !!!next-token;
5494                redo B;
5495            }            }
5496            } else {
5497              !!!cp ('t309');
5498              !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);
5499    
5500            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
5501            ## reprocess            ## reprocess
5502            redo B;            redo B;
5503          } elsif ($self->{insertion_mode} eq 'in frameset') {          }
5504            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5505              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t309.2');
5506                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          ## Stop parsing
5507            last B;
5508                unless (length $token->{data}) {        } else {
5509                  !!!next-token;          die "$0: $token->{type}: Unknown token type";
5510                  redo B;        }
5511                }      } elsif ($self->{insertion_mode} & FRAME_IMS) {
5512              }        if ($token->{type} == CHARACTER_TOKEN) {
5513            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5514              #            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5515            } elsif ($token->{type} eq 'comment') {            
5516              my $comment = $self->{document}->create_comment ($token->{data});            unless (length $token->{data}) {
5517              $self->{open_elements}->[-1]->[0]->append_child ($comment);              !!!cp ('t310');
5518              !!!next-token;              !!!next-token;
5519              redo B;              redo B;
5520            } elsif ($token->{type} eq 'start tag') {            }
5521              if ($token->{tag_name} eq 'frameset') {          }
5522                !!!insert-element ($token->{tag_name}, $token->{attributes});          
5523                !!!next-token;          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
5524                redo B;            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5525              } elsif ($token->{tag_name} eq 'frame') {              !!!cp ('t311');
5526                !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!parse-error (type => 'in frameset:#character', token => $token);
5527                pop @{$self->{open_elements}};            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
5528                !!!next-token;              !!!cp ('t312');
5529                redo B;              !!!parse-error (type => 'after frameset:#character', token => $token);
5530              } elsif ($token->{tag_name} eq 'noframes') {            } else { # "after html frameset"
5531                $in_body->($insert_to_current);              !!!cp ('t313');
5532                redo B;              !!!parse-error (type => 'after html:#character', token => $token);
5533              } else {  
5534                #              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5535              }              ## Reprocess in the "after frameset" insertion mode.
5536            } elsif ($token->{type} eq 'end tag') {              !!!parse-error (type => 'after frameset:#character', token => $token);
             if ($token->{tag_name} eq 'frameset') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html' and  
                   @{$self->{open_elements}} == 1) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
               } else {  
                 pop @{$self->{open_elements}};  
                 !!!next-token;  
               }  
                 
               ## if not inner_html and  
               if ($self->{open_elements}->[-1]->[1] ne 'frameset') {  
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
5537            }            }
5538                        
5539            if (defined $token->{tag_name}) {            ## Ignore the token.
5540              !!!parse-error (type => 'in frameset:'.$token->{tag_name});            if (length $token->{data}) {
5541                !!!cp ('t314');
5542                ## reprocess the rest of characters
5543            } else {            } else {
5544              !!!parse-error (type => 'in frameset:#'.$token->{type});              !!!cp ('t315');
5545                !!!next-token;
5546              }
5547              redo B;
5548            }
5549            
5550            die qq[$0: Character "$token->{data}"];
5551          } elsif ($token->{type} == START_TAG_TOKEN) {
5552            if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5553              !!!cp ('t316');
5554              !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5555    
5556              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5557              ## Process in the "after frameset" insertion mode.
5558            } else {
5559              !!!cp ('t317');
5560            }
5561    
5562            if ($token->{tag_name} eq 'frameset' and
5563                $self->{insertion_mode} == IN_FRAMESET_IM) {
5564              !!!cp ('t318');
5565              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5566              !!!next-token;
5567              redo B;
5568            } elsif ($token->{tag_name} eq 'frame' and
5569                     $self->{insertion_mode} == IN_FRAMESET_IM) {
5570              !!!cp ('t319');
5571              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5572              pop @{$self->{open_elements}};
5573              !!!next-token;
5574              redo B;
5575            } elsif ($token->{tag_name} eq 'noframes') {
5576              !!!cp ('t320');
5577              ## NOTE: As if in body.
5578              $parse_rcdata->(CDATA_CONTENT_MODEL);
5579              redo B;
5580            } else {
5581              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5582                !!!cp ('t321');
5583                !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);
5584              } else {
5585                !!!cp ('t322');
5586                !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);
5587            }            }
5588            ## Ignore the token            ## Ignore the token
5589            !!!next-token;            !!!next-token;
5590            redo B;            redo B;
5591          } elsif ($self->{insertion_mode} eq 'after frameset') {          }
5592            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_TAG_TOKEN) {
5593              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5594                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            !!!cp ('t323');
5595              !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5596    
5597              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5598              ## Process in the "after frameset" insertion mode.
5599            } else {
5600              !!!cp ('t324');
5601            }
5602    
5603            if ($token->{tag_name} eq 'frameset' and
5604                $self->{insertion_mode} == IN_FRAMESET_IM) {
5605              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5606                  @{$self->{open_elements}} == 1) {
5607                !!!cp ('t325');
5608                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5609                ## Ignore the token
5610                !!!next-token;
5611              } else {
5612                !!!cp ('t326');
5613                pop @{$self->{open_elements}};
5614                !!!next-token;
5615              }
5616    
5617                unless (length $token->{data}) {            if (not defined $self->{inner_html_node} and
5618                  !!!next-token;                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
5619                  redo B;              !!!cp ('t327');
5620                }              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5621              } else {
5622                !!!cp ('t328');
5623              }
5624              redo B;
5625            } elsif ($token->{tag_name} eq 'html' and
5626                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
5627              !!!cp ('t329');
5628              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
5629              !!!next-token;
5630              redo B;
5631            } else {
5632              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5633                !!!cp ('t330');
5634                !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);
5635              } else {
5636                !!!cp ('t331');
5637                !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);
5638              }
5639              ## Ignore the token
5640              !!!next-token;
5641              redo B;
5642            }
5643          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5644            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5645                    @{$self->{open_elements}} == 1) { # redundant, maybe
5646              !!!cp ('t331.1');
5647              !!!parse-error (type => 'in body:#eof', token => $token);
5648            } else {
5649              !!!cp ('t331.2');
5650            }
5651            
5652            ## Stop parsing
5653            last B;
5654          } else {
5655            die "$0: $token->{type}: Unknown token type";
5656          }
5657    
5658          ## ISSUE: An issue in spec here
5659        } else {
5660          die "$0: $self->{insertion_mode}: Unknown insertion mode";
5661        }
5662    
5663        ## "in body" insertion mode
5664        if ($token->{type} == START_TAG_TOKEN) {
5665          if ($token->{tag_name} eq 'script') {
5666            !!!cp ('t332');
5667            ## NOTE: This is an "as if in head" code clone
5668            $script_start_tag->();
5669            redo B;
5670          } elsif ($token->{tag_name} eq 'style') {
5671            !!!cp ('t333');
5672            ## NOTE: This is an "as if in head" code clone
5673            $parse_rcdata->(CDATA_CONTENT_MODEL);
5674            redo B;
5675          } elsif ({
5676                    base => 1, link => 1,
5677                   }->{$token->{tag_name}}) {
5678            !!!cp ('t334');
5679            ## NOTE: This is an "as if in head" code clone, only "-t" differs
5680            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5681            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5682            !!!next-token;
5683            redo B;
5684          } elsif ($token->{tag_name} eq 'meta') {
5685            ## NOTE: This is an "as if in head" code clone, only "-t" differs
5686            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5687            my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5688    
5689            unless ($self->{confident}) {
5690              if ($token->{attributes}->{charset}) { ## TODO: And if supported
5691                !!!cp ('t335');
5692                $self->{change_encoding}
5693                    ->($self, $token->{attributes}->{charset}->{value}, $token);
5694                
5695                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5696                    ->set_user_data (manakai_has_reference =>
5697                                         $token->{attributes}->{charset}
5698                                             ->{has_reference});
5699              } elsif ($token->{attributes}->{content}) {
5700                ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
5701                if ($token->{attributes}->{content}->{value}
5702                    =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
5703                        [\x09-\x0D\x20]*=
5704                        [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
5705                        ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
5706                  !!!cp ('t336');
5707                  $self->{change_encoding}
5708                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
5709                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5710                      ->set_user_data (manakai_has_reference =>
5711                                           $token->{attributes}->{content}
5712                                                 ->{has_reference});
5713              }              }
5714              }
5715            } else {
5716              if ($token->{attributes}->{charset}) {
5717                !!!cp ('t337');
5718                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5719                    ->set_user_data (manakai_has_reference =>
5720                                         $token->{attributes}->{charset}
5721                                             ->{has_reference});
5722              }
5723              if ($token->{attributes}->{content}) {
5724                !!!cp ('t338');
5725                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5726                    ->set_user_data (manakai_has_reference =>
5727                                         $token->{attributes}->{content}
5728                                             ->{has_reference});
5729              }
5730            }
5731    
5732              #          !!!next-token;
5733            } elsif ($token->{type} eq 'comment') {          redo B;
5734              my $comment = $self->{document}->create_comment ($token->{data});        } elsif ($token->{tag_name} eq 'title') {
5735              $self->{open_elements}->[-1]->[0]->append_child ($comment);          !!!cp ('t341');
5736              !!!next-token;          ## NOTE: This is an "as if in head" code clone
5737              redo B;          $parse_rcdata->(RCDATA_CONTENT_MODEL);
5738            } elsif ($token->{type} eq 'start tag') {          redo B;
5739              if ($token->{tag_name} eq 'noframes') {        } elsif ($token->{tag_name} eq 'body') {
5740                $in_body->($insert_to_current);          !!!parse-error (type => 'in body:body', token => $token);
5741                redo B;                
5742              } else {          if (@{$self->{open_elements}} == 1 or
5743                #              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
5744              !!!cp ('t342');
5745              ## Ignore the token
5746            } else {
5747              my $body_el = $self->{open_elements}->[1]->[0];
5748              for my $attr_name (keys %{$token->{attributes}}) {
5749                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
5750                  !!!cp ('t343');
5751                  $body_el->set_attribute_ns
5752                    (undef, [undef, $attr_name],
5753                     $token->{attributes}->{$attr_name}->{value});
5754              }              }
5755            } elsif ($token->{type} eq 'end tag') {            }
5756              if ($token->{tag_name} eq 'html') {          }
5757                $phase = 'trailing end';          !!!next-token;
5758            redo B;
5759          } elsif ({
5760                    address => 1, blockquote => 1, center => 1, dir => 1,
5761                    div => 1, dl => 1, fieldset => 1,
5762                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5763                    menu => 1, ol => 1, p => 1, ul => 1,
5764                    pre => 1, listing => 1,
5765                    form => 1,
5766                    table => 1,
5767                    hr => 1,
5768                   }->{$token->{tag_name}}) {
5769            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
5770              !!!cp ('t350');
5771              !!!parse-error (type => 'in form:form', token => $token);
5772              ## Ignore the token
5773              !!!next-token;
5774              redo B;
5775            }
5776    
5777            ## has a p element in scope
5778            INSCOPE: for (reverse @{$self->{open_elements}}) {
5779              if ($_->[1] & P_EL) {
5780                !!!cp ('t344');
5781                !!!back-token;
5782                $token = {type => END_TAG_TOKEN, tag_name => 'p',
5783                          line => $token->{line}, column => $token->{column}};
5784                redo B;
5785              } elsif ($_->[1] & SCOPING_EL) {
5786                !!!cp ('t345');
5787                last INSCOPE;
5788              }
5789            } # INSCOPE
5790              
5791            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5792            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
5793              !!!next-token;
5794              if ($token->{type} == CHARACTER_TOKEN) {
5795                $token->{data} =~ s/^\x0A//;
5796                unless (length $token->{data}) {
5797                  !!!cp ('t346');
5798                !!!next-token;                !!!next-token;
               redo B;  
5799              } else {              } else {
5800                #                !!!cp ('t349');
5801              }              }
5802            } else {            } else {
5803              #              !!!cp ('t348');
5804              }
5805            } elsif ($token->{tag_name} eq 'form') {
5806              !!!cp ('t347.1');
5807              $self->{form_element} = $self->{open_elements}->[-1]->[0];
5808    
5809              !!!next-token;
5810            } elsif ($token->{tag_name} eq 'table') {
5811              !!!cp ('t382');
5812              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
5813              
5814              $self->{insertion_mode} = IN_TABLE_IM;
5815    
5816              !!!next-token;
5817            } elsif ($token->{tag_name} eq 'hr') {
5818              !!!cp ('t386');
5819              pop @{$self->{open_elements}};
5820            
5821              !!!next-token;
5822            } else {
5823              !!!cp ('t347');
5824              !!!next-token;
5825            }
5826            redo B;
5827          } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
5828            ## has a p element in scope
5829            INSCOPE: for (reverse @{$self->{open_elements}}) {
5830              if ($_->[1] & P_EL) {
5831                !!!cp ('t353');
5832                !!!back-token;
5833                $token = {type => END_TAG_TOKEN, tag_name => 'p',
5834                          line => $token->{line}, column => $token->{column}};
5835                redo B;
5836              } elsif ($_->[1] & SCOPING_EL) {
5837                !!!cp ('t354');
5838                last INSCOPE;
5839            }            }
5840            } # INSCOPE
5841                        
5842            if (defined $token->{tag_name}) {          ## Step 1
5843              !!!parse-error (type => 'after frameset:'.$token->{tag_name});          my $i = -1;
5844            my $node = $self->{open_elements}->[$i];
5845            my $li_or_dtdd = {li => {li => 1},
5846                              dt => {dt => 1, dd => 1},
5847                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
5848            LI: {
5849              ## Step 2
5850              if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
5851                if ($i != -1) {
5852                  !!!cp ('t355');
5853                  !!!parse-error (type => 'not closed',
5854                                  value => $self->{open_elements}->[-1]->[0]
5855                                      ->manakai_local_name,
5856                                  token => $token);
5857                } else {
5858                  !!!cp ('t356');
5859                }
5860                splice @{$self->{open_elements}}, $i;
5861                last LI;
5862            } else {            } else {
5863              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t357');
5864              }
5865              
5866              ## Step 3
5867              if (not ($node->[1] & FORMATTING_EL) and
5868                  #not $phrasing_category->{$node->[1]} and
5869                  ($node->[1] & SPECIAL_EL or
5870                   $node->[1] & SCOPING_EL) and
5871                  not ($node->[1] & ADDRESS_EL) and
5872                  not ($node->[1] & DIV_EL)) {
5873                !!!cp ('t358');
5874                last LI;
5875            }            }
5876              
5877              !!!cp ('t359');
5878              ## Step 4
5879              $i--;
5880              $node = $self->{open_elements}->[$i];
5881              redo LI;
5882            } # LI
5883              
5884            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5885            !!!next-token;
5886            redo B;
5887          } elsif ($token->{tag_name} eq 'plaintext') {
5888            ## has a p element in scope
5889            INSCOPE: for (reverse @{$self->{open_elements}}) {
5890              if ($_->[1] & P_EL) {
5891                !!!cp ('t367');
5892                !!!back-token;
5893                $token = {type => END_TAG_TOKEN, tag_name => 'p',
5894                          line => $token->{line}, column => $token->{column}};
5895                redo B;
5896              } elsif ($_->[1] & SCOPING_EL) {
5897                !!!cp ('t368');
5898                last INSCOPE;
5899              }
5900            } # INSCOPE
5901              
5902            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5903              
5904            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
5905              
5906            !!!next-token;
5907            redo B;
5908          } elsif ($token->{tag_name} eq 'a') {
5909            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
5910              my $node = $active_formatting_elements->[$i];
5911              if ($node->[1] & A_EL) {
5912                !!!cp ('t371');
5913                !!!parse-error (type => 'in a:a', token => $token);
5914                
5915                !!!back-token;
5916                $token = {type => END_TAG_TOKEN, tag_name => 'a',
5917                          line => $token->{line}, column => $token->{column}};
5918                $formatting_end_tag->($token);
5919                
5920                AFE2: for (reverse 0..$#$active_formatting_elements) {
5921                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
5922                    !!!cp ('t372');
5923                    splice @$active_formatting_elements, $_, 1;
5924                    last AFE2;
5925                  }
5926                } # AFE2
5927                OE: for (reverse 0..$#{$self->{open_elements}}) {
5928                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
5929                    !!!cp ('t373');
5930                    splice @{$self->{open_elements}}, $_, 1;
5931                    last OE;
5932                  }
5933                } # OE
5934                last AFE;
5935              } elsif ($node->[0] eq '#marker') {
5936                !!!cp ('t374');
5937                last AFE;
5938              }
5939            } # AFE
5940              
5941            $reconstruct_active_formatting_elements->($insert_to_current);
5942    
5943            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5944            push @$active_formatting_elements, $self->{open_elements}->[-1];
5945    
5946            !!!next-token;
5947            redo B;
5948          } elsif ($token->{tag_name} eq 'nobr') {
5949            $reconstruct_active_formatting_elements->($insert_to_current);
5950    
5951            ## has a |nobr| element in scope
5952            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5953              my $node = $self->{open_elements}->[$_];
5954              if ($node->[1] & NOBR_EL) {
5955                !!!cp ('t376');
5956                !!!parse-error (type => 'in nobr:nobr', token => $token);
5957                !!!back-token;
5958                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
5959                          line => $token->{line}, column => $token->{column}};
5960                redo B;
5961              } elsif ($node->[1] & SCOPING_EL) {
5962                !!!cp ('t377');
5963                last INSCOPE;
5964              }
5965            } # INSCOPE
5966            
5967            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5968            push @$active_formatting_elements, $self->{open_elements}->[-1];
5969            
5970            !!!next-token;
5971            redo B;
5972          } elsif ($token->{tag_name} eq 'button') {
5973            ## has a button element in scope
5974            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5975              my $node = $self->{open_elements}->[$_];
5976              if ($node->[1] & BUTTON_EL) {
5977                !!!cp ('t378');
5978                !!!parse-error (type => 'in button:button', token => $token);
5979                !!!back-token;
5980                $token = {type => END_TAG_TOKEN, tag_name => 'button',
5981                          line => $token->{line}, column => $token->{column}};
5982                redo B;
5983              } elsif ($node->[1] & SCOPING_EL) {
5984                !!!cp ('t379');
5985                last INSCOPE;
5986              }
5987            } # INSCOPE
5988              
5989            $reconstruct_active_formatting_elements->($insert_to_current);
5990              
5991            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5992    
5993            ## TODO: associate with $self->{form_element} if defined
5994    
5995            push @$active_formatting_elements, ['#marker', ''];
5996    
5997            !!!next-token;
5998            redo B;
5999          } elsif ({
6000                    xmp => 1,
6001                    iframe => 1,
6002                    noembed => 1,
6003                    noframes => 1,
6004                    noscript => 0, ## TODO: 1 if scripting is enabled
6005                   }->{$token->{tag_name}}) {
6006            if ($token->{tag_name} eq 'xmp') {
6007              !!!cp ('t381');
6008              $reconstruct_active_formatting_elements->($insert_to_current);
6009            } else {
6010              !!!cp ('t399');
6011            }
6012            ## NOTE: There is an "as if in body" code clone.
6013            $parse_rcdata->(CDATA_CONTENT_MODEL);
6014            redo B;
6015          } elsif ($token->{tag_name} eq 'isindex') {
6016            !!!parse-error (type => 'isindex', token => $token);
6017            
6018            if (defined $self->{form_element}) {
6019              !!!cp ('t389');
6020            ## Ignore the token            ## Ignore the token
6021            !!!next-token;            !!!next-token;
6022            redo B;            redo B;
6023            } else {
6024              my $at = $token->{attributes};
6025              my $form_attrs;
6026              $form_attrs->{action} = $at->{action} if $at->{action};
6027              my $prompt_attr = $at->{prompt};
6028              $at->{name} = {name => 'name', value => 'isindex'};
6029              delete $at->{action};
6030              delete $at->{prompt};
6031              my @tokens = (
6032                            {type => START_TAG_TOKEN, tag_name => 'form',
6033                             attributes => $form_attrs,
6034                             line => $token->{line}, column => $token->{column}},
6035                            {type => START_TAG_TOKEN, tag_name => 'hr',
6036                             line => $token->{line}, column => $token->{column}},
6037                            {type => START_TAG_TOKEN, tag_name => 'p',
6038                             line => $token->{line}, column => $token->{column}},
6039                            {type => START_TAG_TOKEN, tag_name => 'label',
6040                             line => $token->{line}, column => $token->{column}},
6041                           );
6042              if ($prompt_attr) {
6043                !!!cp ('t390');
6044                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
6045                               #line => $token->{line}, column => $token->{column},
6046                              };
6047              } else {
6048                !!!cp ('t391');
6049                push @tokens, {type => CHARACTER_TOKEN,
6050                               data => 'This is a searchable index. Insert your search keywords here: ',
6051                               #line => $token->{line}, column => $token->{column},
6052                              }; # SHOULD
6053                ## TODO: make this configurable
6054              }
6055              push @tokens,
6056                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
6057                             line => $token->{line}, column => $token->{column}},
6058                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
6059                            {type => END_TAG_TOKEN, tag_name => 'label',
6060                             line => $token->{line}, column => $token->{column}},
6061                            {type => END_TAG_TOKEN, tag_name => 'p',
6062                             line => $token->{line}, column => $token->{column}},
6063                            {type => START_TAG_TOKEN, tag_name => 'hr',
6064                             line => $token->{line}, column => $token->{column}},
6065                            {type => END_TAG_TOKEN, tag_name => 'form',
6066                             line => $token->{line}, column => $token->{column}};
6067              $token = shift @tokens;
6068              !!!back-token (@tokens);
6069              redo B;
6070            }
6071          } elsif ($token->{tag_name} eq 'textarea') {
6072            my $tag_name = $token->{tag_name};
6073            my $el;
6074            !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);
6075            
6076            ## TODO: $self->{form_element} if defined
6077            $self->{content_model} = RCDATA_CONTENT_MODEL;
6078            delete $self->{escape}; # MUST
6079            
6080            $insert->($el);
6081            
6082            my $text = '';
6083            !!!next-token;
6084            if ($token->{type} == CHARACTER_TOKEN) {
6085              $token->{data} =~ s/^\x0A//;
6086              unless (length $token->{data}) {
6087                !!!cp ('t392');
6088                !!!next-token;
6089              } else {
6090                !!!cp ('t393');
6091              }
6092            } else {
6093              !!!cp ('t394');
6094            }
6095            while ($token->{type} == CHARACTER_TOKEN) {
6096              !!!cp ('t395');
6097              $text .= $token->{data};
6098              !!!next-token;
6099            }
6100            if (length $text) {
6101              !!!cp ('t396');
6102              $el->manakai_append_text ($text);
6103            }
6104            
6105            $self->{content_model} = PCDATA_CONTENT_MODEL;
6106            
6107            if ($token->{type} == END_TAG_TOKEN and
6108                $token->{tag_name} eq $tag_name) {
6109              !!!cp ('t397');
6110              ## Ignore the token
6111            } else {
6112              !!!cp ('t398');
6113              !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
6114            }
6115            !!!next-token;
6116            redo B;
6117          } elsif ({
6118                    caption => 1, col => 1, colgroup => 1, frame => 1,
6119                    frameset => 1, head => 1, option => 1, optgroup => 1,
6120                    tbody => 1, td => 1, tfoot => 1, th => 1,
6121                    thead => 1, tr => 1,
6122                   }->{$token->{tag_name}}) {
6123            !!!cp ('t401');
6124            !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);
6125            ## Ignore the token
6126            !!!next-token;
6127            redo B;
6128            
6129            ## ISSUE: An issue on HTML5 new elements in the spec.
6130          } else {
6131            if ($token->{tag_name} eq 'image') {
6132              !!!cp ('t384');
6133              !!!parse-error (type => 'image', token => $token);
6134              $token->{tag_name} = 'img';
6135            } else {
6136              !!!cp ('t385');
6137            }
6138    
6139            ## NOTE: There is an "as if <br>" code clone.
6140            $reconstruct_active_formatting_elements->($insert_to_current);
6141            
6142            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6143    
6144            ## ISSUE: An issue in spec there          if ({
6145                 applet => 1, marquee => 1, object => 1,
6146                }->{$token->{tag_name}}) {
6147              !!!cp ('t380');
6148              push @$active_formatting_elements, ['#marker', ''];
6149            } elsif ({
6150                      b => 1, big => 1, em => 1, font => 1, i => 1,
6151                      s => 1, small => 1, strile => 1,
6152                      strong => 1, tt => 1, u => 1,
6153                     }->{$token->{tag_name}}) {
6154              !!!cp ('t375');
6155              push @$active_formatting_elements, $self->{open_elements}->[-1];
6156            } elsif ($token->{tag_name} eq 'input') {
6157              !!!cp ('t388');
6158              ## TODO: associate with $self->{form_element} if defined
6159              pop @{$self->{open_elements}};
6160            } elsif ({
6161                      area => 1, basefont => 1, bgsound => 1, br => 1,
6162                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
6163                      #image => 1,
6164                     }->{$token->{tag_name}}) {
6165              !!!cp ('t388.1');
6166              pop @{$self->{open_elements}};
6167            } elsif ($token->{tag_name} eq 'select') {
6168              ## TODO: associate with $self->{form_element} if defined
6169            
6170              if ($self->{insertion_mode} & TABLE_IMS or
6171                  $self->{insertion_mode} & BODY_TABLE_IMS or
6172                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6173                !!!cp ('t400.1');
6174                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
6175              } else {
6176                !!!cp ('t400.2');
6177                $self->{insertion_mode} = IN_SELECT_IM;
6178              }
6179          } else {          } else {
6180            die "$0: $self->{insertion_mode}: Unknown insertion mode";            !!!cp ('t402');
6181          }          }
6182            
6183            !!!next-token;
6184            redo B;
6185        }        }
6186      } elsif ($phase eq 'trailing end') {      } elsif ($token->{type} == END_TAG_TOKEN) {
6187        ## states in the main stage is preserved yet # MUST        if ($token->{tag_name} eq 'body') {
6188                  ## has a |body| element in scope
6189        if ($token->{type} eq 'DOCTYPE') {          my $i;
6190          !!!parse-error (type => 'after html:#DOCTYPE');          INSCOPE: {
6191          ## Ignore the token            for (reverse @{$self->{open_elements}}) {
6192                if ($_->[1] & BODY_EL) {
6193                  !!!cp ('t405');
6194                  $i = $_;
6195                  last INSCOPE;
6196                } elsif ($_->[1] & SCOPING_EL) {
6197                  !!!cp ('t405.1');
6198                  last;
6199                }
6200              }
6201    
6202              !!!parse-error (type => 'start tag not allowed',
6203                              value => $token->{tag_name}, token => $token);
6204              ## NOTE: Ignore the token.
6205              !!!next-token;
6206              redo B;
6207            } # INSCOPE
6208    
6209            for (@{$self->{open_elements}}) {
6210              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
6211                !!!cp ('t403');
6212                !!!parse-error (type => 'not closed',
6213                                value => $_->[0]->manakai_local_name,
6214                                token => $token);
6215                last;
6216              } else {
6217                !!!cp ('t404');
6218              }
6219            }
6220    
6221            $self->{insertion_mode} = AFTER_BODY_IM;
6222          !!!next-token;          !!!next-token;
6223          redo B;          redo B;
6224        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{tag_name} eq 'html') {
6225          my $comment = $self->{document}->create_comment ($token->{data});          ## TODO: Update this code.  It seems that the code below is not
6226          $self->{document}->append_child ($comment);          ## up-to-date, though it has same effect as speced.
6227            if (@{$self->{open_elements}} > 1 and
6228                $self->{open_elements}->[1]->[1] & BODY_EL) {
6229              ## ISSUE: There is an issue in the spec.
6230              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
6231                !!!cp ('t406');
6232                !!!parse-error (type => 'not closed',
6233                                value => $self->{open_elements}->[1]->[0]
6234                                    ->manakai_local_name,
6235                                token => $token);
6236              } else {
6237                !!!cp ('t407');
6238              }
6239              $self->{insertion_mode} = AFTER_BODY_IM;
6240              ## reprocess
6241              redo B;
6242            } else {
6243              !!!cp ('t408');
6244              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6245              ## Ignore the token
6246              !!!next-token;
6247              redo B;
6248            }
6249          } elsif ({
6250                    address => 1, blockquote => 1, center => 1, dir => 1,
6251                    div => 1, dl => 1, fieldset => 1, listing => 1,
6252                    menu => 1, ol => 1, pre => 1, ul => 1,
6253                    dd => 1, dt => 1, li => 1,
6254                    applet => 1, button => 1, marquee => 1, object => 1,
6255                   }->{$token->{tag_name}}) {
6256            ## has an element in scope
6257            my $i;
6258            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6259              my $node = $self->{open_elements}->[$_];
6260              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6261                !!!cp ('t410');
6262                $i = $_;
6263                last INSCOPE;
6264              } elsif ($node->[1] & SCOPING_EL) {
6265                !!!cp ('t411');
6266                last INSCOPE;
6267              }
6268            } # INSCOPE
6269    
6270            unless (defined $i) { # has an element in scope
6271              !!!cp ('t413');
6272              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6273            } else {
6274              ## Step 1. generate implied end tags
6275              while ({
6276                      dd => ($token->{tag_name} ne 'dd'),
6277                      dt => ($token->{tag_name} ne 'dt'),
6278                      li => ($token->{tag_name} ne 'li'),
6279                      p => 1,
6280                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
6281                !!!cp ('t409');
6282                pop @{$self->{open_elements}};
6283              }
6284    
6285              ## Step 2.
6286              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6287                      ne $token->{tag_name}) {
6288                !!!cp ('t412');
6289                !!!parse-error (type => 'not closed',
6290                                value => $self->{open_elements}->[-1]->[0]
6291                                    ->manakai_local_name,
6292                                token => $token);
6293              } else {
6294                !!!cp ('t414');
6295              }
6296    
6297              ## Step 3.
6298              splice @{$self->{open_elements}}, $i;
6299    
6300              ## Step 4.
6301              $clear_up_to_marker->()
6302                  if {
6303                    applet => 1, button => 1, marquee => 1, object => 1,
6304                  }->{$token->{tag_name}};
6305            }
6306          !!!next-token;          !!!next-token;
6307          redo B;          redo B;
6308        } elsif ($token->{type} eq 'character') {        } elsif ($token->{tag_name} eq 'form') {
6309          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          undef $self->{form_element};
6310            my $data = $1;  
6311            ## As if in the main phase.          ## has an element in scope
6312            ## NOTE: The insertion mode in the main phase          my $i;
6313            ## just before the phase has been changed to the trailing          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6314            ## end phase is either "after body" or "after frameset".            my $node = $self->{open_elements}->[$_];
6315            $reconstruct_active_formatting_elements->($insert_to_current)            if ($node->[1] & FORM_EL) {
6316              if $phase eq 'main';              !!!cp ('t418');
6317                $i = $_;
6318                last INSCOPE;
6319              } elsif ($node->[1] & SCOPING_EL) {
6320                !!!cp ('t419');
6321                last INSCOPE;
6322              }
6323            } # INSCOPE
6324    
6325            unless (defined $i) { # has an element in scope
6326              !!!cp ('t421');
6327              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6328            } else {
6329              ## Step 1. generate implied end tags
6330              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6331                !!!cp ('t417');
6332                pop @{$self->{open_elements}};
6333              }
6334                        
6335            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
6336              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6337                      ne $token->{tag_name}) {
6338                !!!cp ('t417.1');
6339                !!!parse-error (type => 'not closed',
6340                                value => $self->{open_elements}->[-1]->[0]
6341                                    ->manakai_local_name,
6342                                token => $token);
6343              } else {
6344                !!!cp ('t420');
6345              }  
6346                        
6347            unless (length $token->{data}) {            ## Step 3.
6348              !!!next-token;            splice @{$self->{open_elements}}, $i;
6349              redo B;          }
6350    
6351            !!!next-token;
6352            redo B;
6353          } elsif ({
6354                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6355                   }->{$token->{tag_name}}) {
6356            ## has an element in scope
6357            my $i;
6358            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6359              my $node = $self->{open_elements}->[$_];
6360              if ($node->[1] & HEADING_EL) {
6361                !!!cp ('t423');
6362                $i = $_;
6363                last INSCOPE;
6364              } elsif ($node->[1] & SCOPING_EL) {
6365                !!!cp ('t424');
6366                last INSCOPE;
6367              }
6368            } # INSCOPE
6369    
6370            unless (defined $i) { # has an element in scope
6371              !!!cp ('t425.1');
6372              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6373            } else {
6374              ## Step 1. generate implied end tags
6375              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6376                !!!cp ('t422');
6377                pop @{$self->{open_elements}};
6378            }            }
6379              
6380              ## Step 2.
6381              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6382                      ne $token->{tag_name}) {
6383                !!!cp ('t425');
6384                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6385              } else {
6386                !!!cp ('t426');
6387              }
6388    
6389              ## Step 3.
6390              splice @{$self->{open_elements}}, $i;
6391          }          }
6392            
6393            !!!next-token;
6394            redo B;
6395          } elsif ($token->{tag_name} eq 'p') {
6396            ## has an element in scope
6397            my $i;
6398            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6399              my $node = $self->{open_elements}->[$_];
6400              if ($node->[1] & P_EL) {
6401                !!!cp ('t410.1');
6402                $i = $_;
6403                last INSCOPE;
6404              } elsif ($node->[1] & SCOPING_EL) {
6405                !!!cp ('t411.1');
6406                last INSCOPE;
6407              }
6408            } # INSCOPE
6409    
6410          !!!parse-error (type => 'after html:#character');          if (defined $i) {
6411          $phase = 'main';            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6412          ## reprocess                    ne $token->{tag_name}) {
6413                !!!cp ('t412.1');
6414                !!!parse-error (type => 'not closed',
6415                                value => $self->{open_elements}->[-1]->[0]
6416                                    ->manakai_local_name,
6417                                token => $token);
6418              } else {
6419                !!!cp ('t414.1');
6420              }
6421    
6422              splice @{$self->{open_elements}}, $i;
6423            } else {
6424              !!!cp ('t413.1');
6425              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6426    
6427              !!!cp ('t415.1');
6428              ## As if <p>, then reprocess the current token
6429              my $el;
6430              !!!create-element ($el, 'p',, $token);
6431              $insert->($el);
6432              ## NOTE: Not inserted into |$self->{open_elements}|.
6433            }
6434    
6435            !!!next-token;
6436          redo B;          redo B;
6437        } elsif ($token->{type} eq 'start tag' or        } elsif ({
6438                 $token->{type} eq 'end tag') {                  a => 1,
6439          !!!parse-error (type => 'after html:'.$token->{tag_name});                  b => 1, big => 1, em => 1, font => 1, i => 1,
6440          $phase = 'main';                  nobr => 1, s => 1, small => 1, strile => 1,
6441          ## reprocess                  strong => 1, tt => 1, u => 1,
6442                   }->{$token->{tag_name}}) {
6443            !!!cp ('t427');
6444            $formatting_end_tag->($token);
6445          redo B;          redo B;
6446        } elsif ($token->{type} eq 'end-of-file') {        } elsif ($token->{tag_name} eq 'br') {
6447          ## Stop parsing          !!!cp ('t428');
6448          last B;          !!!parse-error (type => 'unmatched end tag:br', token => $token);
6449    
6450            ## As if <br>
6451            $reconstruct_active_formatting_elements->($insert_to_current);
6452            
6453            my $el;
6454            !!!create-element ($el, 'br',, $token);
6455            $insert->($el);
6456            
6457            ## Ignore the token.
6458            !!!next-token;
6459            redo B;
6460          } elsif ({
6461                    caption => 1, col => 1, colgroup => 1, frame => 1,
6462                    frameset => 1, head => 1, option => 1, optgroup => 1,
6463                    tbody => 1, td => 1, tfoot => 1, th => 1,
6464                    thead => 1, tr => 1,
6465                    area => 1, basefont => 1, bgsound => 1,
6466                    embed => 1, hr => 1, iframe => 1, image => 1,
6467                    img => 1, input => 1, isindex => 1, noembed => 1,
6468                    noframes => 1, param => 1, select => 1, spacer => 1,
6469                    table => 1, textarea => 1, wbr => 1,
6470                    noscript => 0, ## TODO: if scripting is enabled
6471                   }->{$token->{tag_name}}) {
6472            !!!cp ('t429');
6473            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6474            ## Ignore the token
6475            !!!next-token;
6476            redo B;
6477            
6478            ## ISSUE: Issue on HTML5 new elements in spec
6479            
6480        } else {        } else {
6481          die "$0: $token->{type}: Unknown token";          ## Step 1
6482            my $node_i = -1;
6483            my $node = $self->{open_elements}->[$node_i];
6484    
6485            ## Step 2
6486            S2: {
6487              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6488                ## Step 1
6489                ## generate implied end tags
6490                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6491                  !!!cp ('t430');
6492                  ## ISSUE: Can this case be reached?
6493                  pop @{$self->{open_elements}};
6494                }
6495            
6496                ## Step 2
6497                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6498                        ne $token->{tag_name}) {
6499                  !!!cp ('t431');
6500                  ## NOTE: <x><y></x>
6501                  !!!parse-error (type => 'not closed',
6502                                  value => $self->{open_elements}->[-1]->[0]
6503                                      ->manakai_local_name,
6504                                  token => $token);
6505                } else {
6506                  !!!cp ('t432');
6507                }
6508                
6509                ## Step 3
6510                splice @{$self->{open_elements}}, $node_i;
6511    
6512                !!!next-token;
6513                last S2;
6514              } else {
6515                ## Step 3
6516                if (not ($node->[1] & FORMATTING_EL) and
6517                    #not $phrasing_category->{$node->[1]} and
6518                    ($node->[1] & SPECIAL_EL or
6519                     $node->[1] & SCOPING_EL)) {
6520                  !!!cp ('t433');
6521                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6522                  ## Ignore the token
6523                  !!!next-token;
6524                  last S2;
6525                }
6526    
6527                !!!cp ('t434');
6528              }
6529              
6530              ## Step 4
6531              $node_i--;
6532              $node = $self->{open_elements}->[$node_i];
6533              
6534              ## Step 5;
6535              redo S2;
6536            } # S2
6537            redo B;
6538        }        }
6539      }      }
6540        redo B;
6541    } # B    } # B
6542    
6543    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 4771  sub set_inner_html ($$$) { Line 6551  sub set_inner_html ($$$) {
6551    my $s = \$_[0];    my $s = \$_[0];
6552    my $onerror = $_[1];    my $onerror = $_[1];
6553    
6554      ## ISSUE: Should {confident} be true?
6555    
6556    my $nt = $node->node_type;    my $nt = $node->node_type;
6557    if ($nt == 9) {    if ($nt == 9) {
6558      # MUST      # MUST
# Line 4793  sub set_inner_html ($$$) { Line 6575  sub set_inner_html ($$$) {
6575      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
6576    
6577      ## Step 1 # MUST      ## Step 1 # MUST
6578      my $doc = $node->owner_document->implementation->create_document;      my $this_doc = $node->owner_document;
6579      ## TODO: Mark as HTML document      my $doc = $this_doc->implementation->create_document;
6580        $doc->manakai_is_html (1);
6581      my $p = $class->new;      my $p = $class->new;
6582      $p->{document} = $doc;      $p->{document} = $doc;
6583    
6584      ## Step 9 # MUST      ## Step 8 # MUST
6585      my $i = 0;      my $i = 0;
6586      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
6587      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
6588      $p->{set_next_input_character} = sub {      $p->{set_next_char} = sub {
6589        my $self = shift;        my $self = shift;
6590        $self->{next_input_character} = -1 and return if $i >= length $$s;  
6591        $self->{next_input_character} = ord substr $$s, $i++, 1;        pop @{$self->{prev_char}};
6592        $column++;        unshift @{$self->{prev_char}}, $self->{next_char};
6593          
6594        if ($self->{next_input_character} == 0x000D) { # CR        $self->{next_char} = -1 and return if $i >= length $$s;
6595          if ($i >= length $$s) {        $self->{next_char} = ord substr $$s, $i++, 1;
6596            #  
6597          } else {        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
6598            my $next_char = ord substr $$s, $i++, 1;        $p->{column}++;
6599            if ($next_char == 0x000A) { # LF  
6600              #        if ($self->{next_char} == 0x000A) { # LF
6601            } else {          $p->{line}++;
6602              push @{$self->{char}}, $next_char;          $p->{column} = 0;
6603            }          !!!cp ('i1');
6604          }        } elsif ($self->{next_char} == 0x000D) { # CR
6605          $self->{next_input_character} = 0x000A; # LF # MUST          $i++ if substr ($$s, $i, 1) eq "\x0A";
6606          $line++;          $self->{next_char} = 0x000A; # LF # MUST
6607          $column = -1;          $p->{line}++;
6608        } elsif ($self->{next_input_character} > 0x10FFFF) {          $p->{column} = 0;
6609          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          !!!cp ('i2');
6610        } elsif ($self->{next_input_character} == 0x0000) { # NULL        } elsif ($self->{next_char} > 0x10FFFF) {
6611          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6612            !!!cp ('i3');
6613          } elsif ($self->{next_char} == 0x0000) { # NULL
6614            !!!cp ('i4');
6615            !!!parse-error (type => 'NULL');
6616            $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6617        }        }
6618      };      };
6619        $p->{prev_char} = [-1, -1, -1];
6620        $p->{next_char} = -1;
6621            
6622      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
6623        my (%opt) = @_;        my (%opt) = @_;
6624        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
6625          my $column = $opt{column};
6626          if (defined $opt{token} and defined $opt{token}->{line}) {
6627            $line = $opt{token}->{line};
6628            $column = $opt{token}->{column};
6629          }
6630          warn "Parse error ($opt{type}) at line $line column $column\n";
6631      };      };
6632      $p->{parse_error} = sub {      $p->{parse_error} = sub {
6633        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
6634      };      };
6635            
6636      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
6637      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
6638    
6639      ## Step 2      ## Step 2
6640      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
6641      $p->{content_model_flag} = {      $p->{content_model} = {
6642        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
6643        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
6644        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
6645        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
6646        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
6647        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
6648        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
6649        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
6650        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
6651        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
6652      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
6653         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
6654            unless defined $p->{content_model};
6655            ## ISSUE: What is "the name of the element"? local name?
6656    
6657      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
6658          ## TODO: Foreign element OK?
6659    
6660      ## Step 4      ## Step 3
6661      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
6662        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
6663    
6664      ## Step 5 # MUST      ## Step 4 # MUST
6665      $doc->append_child ($root);      $doc->append_child ($root);
6666    
6667      ## Step 6 # MUST      ## Step 5 # MUST
6668      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
6669    
6670      undef $p->{head_element};      undef $p->{head_element};
6671    
6672      ## Step 7 # MUST      ## Step 6 # MUST
6673      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
6674    
6675      ## Step 8 # MUST      ## Step 7 # MUST
6676      my $anode = $node;      my $anode = $node;
6677      AN: while (defined $anode) {      AN: while (defined $anode) {
6678        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
6679          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
6680          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
6681            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
6682                !!!cp ('i5');
6683              $p->{form_element} = $anode;              $p->{form_element} = $anode;
6684              last AN;              last AN;
6685            }            }
# Line 4888  sub set_inner_html ($$$) { Line 6688  sub set_inner_html ($$$) {
6688        $anode = $anode->parent_node;        $anode = $anode->parent_node;
6689      } # AN      } # AN
6690            
6691      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
6692      {      {
6693        my $self = $p;        my $self = $p;
6694        !!!next-token;        !!!next-token;
6695      }      }
6696      $p->_tree_construction_main;      $p->_tree_construction_main;
6697    
6698      ## Step 11 # MUST      ## Step 10 # MUST
6699      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
6700      for (@cn) {      for (@cn) {
6701        $node->remove_child ($_);        $node->remove_child ($_);
6702      }      }
6703      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
6704    
6705      ## Step 12 # MUST      ## Step 11 # MUST
6706      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
6707      for (@cn) {      for (@cn) {
6708          $this_doc->adopt_node ($_);
6709        $node->append_child ($_);        $node->append_child ($_);
6710      }      }
6711      ## ISSUE: adopt_node? mutation events?      ## ISSUE: mutation events?
6712    
6713      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
6714    
6715        delete $p->{parse_error}; # delete loop
6716    } else {    } else {
6717      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
6718    }    }
# Line 4918  sub set_inner_html ($$$) { Line 6720  sub set_inner_html ($$$) {
6720    
6721  } # tree construction stage  } # tree construction stage
6722    
6723  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
6724    my (undef, $node, $on_error) = @_;  push our @ISA, 'Error';
   
   ## Step 1  
   my $s = '';  
   
   my $in_cdata;  
   my $parent = $node;  
   while (defined $parent) {  
     if ($parent->node_type == 1 and  
         $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and  
         {  
           style => 1, script => 1, xmp => 1, iframe => 1,  
           noembed => 1, noframes => 1, noscript => 1,  
         }->{$parent->local_name}) { ## TODO: case thingy  
       $in_cdata = 1;  
     }  
     $parent = $parent->parent_node;  
   }  
   
   ## Step 2  
   my @node = @{$node->child_nodes};  
   C: while (@node) {  
     my $child = shift @node;  
     unless (ref $child) {  
       if ($child eq 'cdata-out') {  
         $in_cdata = 0;  
       } else {  
         $s .= $child; # end tag  
       }  
       next C;  
     }  
       
     my $nt = $child->node_type;  
     if ($nt == 1) { # Element  
       my $tag_name = lc $child->tag_name; ## ISSUE: Definition of "lowercase"  
       $s .= '<' . $tag_name;  
   
       ## ISSUE: Non-html elements  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = lc $attr->name; ## ISSUE: Definition of "lowercase"  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
       }->{$tag_name}) {  
         unshift @node, 'cdata-out';  
         $in_cdata = 1;  
       }  
   
       unshift @node, @{$child->child_nodes}, '</' . $tag_name . '>';  
     } elsif ($nt == 3 or $nt == 4) {  
       if ($in_cdata) {  
         $s .= $child->data;  
       } else {  
         my $value = $child->data;  
         $value =~ s/&/&amp;/g;  
         $value =~ s/</&lt;/g;  
         $value =~ s/>/&gt;/g;  
         $value =~ s/"/&quot;/g;  
         $s .= $value;  
       }  
     } elsif ($nt == 8) {  
       $s .= '<!--' . $child->data . '-->';  
     } elsif ($nt == 10) {  
       $s .= '<!DOCTYPE ' . $child->name . '>';  
     } elsif ($nt == 5) { # entrefs  
       push @node, @{$child->child_nodes};  
     } else {  
       $on_error->($child) if defined $on_error;  
     }  
     ## ISSUE: This code does not support PIs.  
   } # C  
     
   ## Step 3  
   return \$s;  
 } # get_inner_html  
6725    
6726  1;  1;
6727  # $Date$  # $Date$

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.124

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24