/[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.31 by wakaba, Sat Jun 30 14:13:19 2007 UTC revision 1.59 by wakaba, Sat Sep 8 01:31:44 2007 UTC
# Line 150  sub new ($) { Line 150  sub new ($) {
150    return $self;    return $self;
151  } # new  } # new
152    
153    sub CM_ENTITY () { 0b001 } # & markup in data
154    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
155    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
156    
157    sub PLAINTEXT_CONTENT_MODEL () { 0 }
158    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
159    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
160    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
161    
162    sub DATA_STATE () { 0 }
163    sub ENTITY_DATA_STATE () { 1 }
164    sub TAG_OPEN_STATE () { 2 }
165    sub CLOSE_TAG_OPEN_STATE () { 3 }
166    sub TAG_NAME_STATE () { 4 }
167    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
168    sub ATTRIBUTE_NAME_STATE () { 6 }
169    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
170    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
171    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
172    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
173    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
174    sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
175    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
176    sub COMMENT_START_STATE () { 14 }
177    sub COMMENT_START_DASH_STATE () { 15 }
178    sub COMMENT_STATE () { 16 }
179    sub COMMENT_END_STATE () { 17 }
180    sub COMMENT_END_DASH_STATE () { 18 }
181    sub BOGUS_COMMENT_STATE () { 19 }
182    sub DOCTYPE_STATE () { 20 }
183    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
184    sub DOCTYPE_NAME_STATE () { 22 }
185    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
186    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
187    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
188    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
189    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
190    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
191    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
192    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
193    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
194    sub BOGUS_DOCTYPE_STATE () { 32 }
195    
196    sub DOCTYPE_TOKEN () { 1 }
197    sub COMMENT_TOKEN () { 2 }
198    sub START_TAG_TOKEN () { 3 }
199    sub END_TAG_TOKEN () { 4 }
200    sub END_OF_FILE_TOKEN () { 5 }
201    sub CHARACTER_TOKEN () { 6 }
202    
203    sub AFTER_HTML_IMS () { 0b100 }
204    sub HEAD_IMS ()       { 0b1000 }
205    sub BODY_IMS ()       { 0b10000 }
206    sub BODY_TABLE_IMS () { 0b100000 }
207    sub TABLE_IMS ()      { 0b1000000 }
208    sub ROW_IMS ()        { 0b10000000 }
209    sub BODY_AFTER_IMS () { 0b100000000 }
210    sub FRAME_IMS ()      { 0b1000000000 }
211    
212    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
213    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
214    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
215    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
216    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
217    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
218    sub IN_BODY_IM () { BODY_IMS }
219    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
220    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
221    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
222    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
223    sub IN_TABLE_IM () { TABLE_IMS }
224    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
225    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
226    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
227    sub IN_SELECT_IM () { 0b01 }
228    sub IN_COLUMN_GROUP_IM () { 0b10 }
229    
230  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
231    
232  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
233    my $self = shift;    my $self = shift;
234    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
235    $self->{content_model_flag} = 'PCDATA'; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
236    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE
237    undef $self->{current_attribute};    undef $self->{current_attribute};
238    undef $self->{last_emitted_start_tag_name};    undef $self->{last_emitted_start_tag_name};
# Line 168  sub _initialize_tokenizer ($) { Line 245  sub _initialize_tokenizer ($) {
245  } # _initialize_tokenizer  } # _initialize_tokenizer
246    
247  ## A token has:  ## A token has:
248  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
249  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
250  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
251  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
252  ##   ->{system_identifier} (DOCTYPE)  ##   ->{public_identifier} (DOCTYPE_TOKEN)
253  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{system_identifier} (DOCTYPE_TOKEN)
254  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)
255  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
256    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
257    
258  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
259    
# Line 185  sub _initialize_tokenizer ($) { Line 263  sub _initialize_tokenizer ($) {
263  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
264  ## and removed from the list.  ## and removed from the list.
265    
266    ## NOTE: HTML5 "Writing HTML documents" section, applied to
267    ## documents and not to user agents and conformance checkers,
268    ## contains some requirements that are not detected by the
269    ## parsing algorithm:
270    ## - Some requirements on character encoding declarations. ## TODO
271    ## - "Elements MUST NOT contain content that their content model disallows."
272    ##   ... Some are parse error, some are not (will be reported by c.c.).
273    ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO
274    ## - Text (in elements, attributes, and comments) SHOULD NOT contain
275    ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)
276    
277    ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot
278    ## be detected by the HTML5 parsing algorithm:
279    ## - Text,
280    
281  sub _get_next_token ($) {  sub _get_next_token ($) {
282    my $self = shift;    my $self = shift;
283    if (@{$self->{token}}) {    if (@{$self->{token}}) {
# Line 192  sub _get_next_token ($) { Line 285  sub _get_next_token ($) {
285    }    }
286    
287    A: {    A: {
288      if ($self->{state} eq 'data') {      if ($self->{state} == DATA_STATE) {
289        if ($self->{next_input_character} == 0x0026) { # &        if ($self->{next_input_character} == 0x0026) { # &
290          if ($self->{content_model_flag} eq 'PCDATA' or          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA
291              $self->{content_model_flag} eq 'RCDATA') {            $self->{state} = ENTITY_DATA_STATE;
           $self->{state} = 'entity data';  
292            !!!next-input-character;            !!!next-input-character;
293            redo A;            redo A;
294          } else {          } else {
295            #            #
296          }          }
297        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_input_character} == 0x002D) { # -
298          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
             $self->{content_model_flag} eq 'CDATA') {  
299            unless ($self->{escape}) {            unless ($self->{escape}) {
300              if ($self->{prev_input_character}->[0] == 0x002D and # -              if ($self->{prev_input_character}->[0] == 0x002D and # -
301                  $self->{prev_input_character}->[1] == 0x0021 and # !                  $self->{prev_input_character}->[1] == 0x0021 and # !
# Line 216  sub _get_next_token ($) { Line 307  sub _get_next_token ($) {
307                    
308          #          #
309        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{next_input_character} == 0x003C) { # <
310          if ($self->{content_model_flag} eq 'PCDATA' or          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
311              (($self->{content_model_flag} eq 'CDATA' or              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
               $self->{content_model_flag} eq 'RCDATA') and  
312               not $self->{escape})) {               not $self->{escape})) {
313            $self->{state} = 'tag open';            $self->{state} = TAG_OPEN_STATE;
314            !!!next-input-character;            !!!next-input-character;
315            redo A;            redo A;
316          } else {          } else {
# Line 228  sub _get_next_token ($) { Line 318  sub _get_next_token ($) {
318          }          }
319        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
320          if ($self->{escape} and          if ($self->{escape} and
321              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
              $self->{content_model_flag} eq 'CDATA')) {  
322            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{prev_input_character}->[0] == 0x002D and # -
323                $self->{prev_input_character}->[1] == 0x002D) { # -                $self->{prev_input_character}->[1] == 0x002D) { # -
324              delete $self->{escape};              delete $self->{escape};
# Line 238  sub _get_next_token ($) { Line 327  sub _get_next_token ($) {
327                    
328          #          #
329        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
330          !!!emit ({type => 'end-of-file'});          !!!emit ({type => END_OF_FILE_TOKEN});
331          last A; ## TODO: ok?          last A; ## TODO: ok?
332        }        }
333        # Anything else        # Anything else
334        my $token = {type => 'character',        my $token = {type => CHARACTER_TOKEN,
335                     data => chr $self->{next_input_character}};                     data => chr $self->{next_input_character}};
336        ## Stay in the data state        ## Stay in the data state
337        !!!next-input-character;        !!!next-input-character;
# Line 250  sub _get_next_token ($) { Line 339  sub _get_next_token ($) {
339        !!!emit ($token);        !!!emit ($token);
340    
341        redo A;        redo A;
342      } elsif ($self->{state} eq 'entity data') {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
343        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
344                
345        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);
346    
347        $self->{state} = 'data';        $self->{state} = DATA_STATE;
348        # next-input-character is already done        # next-input-character is already done
349    
350        unless (defined $token) {        unless (defined $token) {
351          !!!emit ({type => 'character', data => '&'});          !!!emit ({type => CHARACTER_TOKEN, data => '&'});
352        } else {        } else {
353          !!!emit ($token);          !!!emit ($token);
354        }        }
355    
356        redo A;        redo A;
357      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
358        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
           $self->{content_model_flag} eq 'CDATA') {  
359          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{next_input_character} == 0x002F) { # /
360            !!!next-input-character;            !!!next-input-character;
361            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
362            redo A;            redo A;
363          } else {          } else {
364            ## reconsume            ## reconsume
365            $self->{state} = 'data';            $self->{state} = DATA_STATE;
366    
367            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<'});
368    
369            redo A;            redo A;
370          }          }
371        } elsif ($self->{content_model_flag} eq 'PCDATA') {        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
372          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{next_input_character} == 0x0021) { # !
373            $self->{state} = 'markup declaration open';            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
374            !!!next-input-character;            !!!next-input-character;
375            redo A;            redo A;
376          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{next_input_character} == 0x002F) { # /
377            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
378            !!!next-input-character;            !!!next-input-character;
379            redo A;            redo A;
380          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_input_character} and
381                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{next_input_character} <= 0x005A) { # A..Z
382            $self->{current_token}            $self->{current_token}
383              = {type => 'start tag',              = {type => START_TAG_TOKEN,
384                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{next_input_character} + 0x0020)};
385            $self->{state} = 'tag name';            $self->{state} = TAG_NAME_STATE;
386            !!!next-input-character;            !!!next-input-character;
387            redo A;            redo A;
388          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_input_character} and
389                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{next_input_character} <= 0x007A) { # a..z
390            $self->{current_token} = {type => 'start tag',            $self->{current_token} = {type => START_TAG_TOKEN,
391                              tag_name => chr ($self->{next_input_character})};                              tag_name => chr ($self->{next_input_character})};
392            $self->{state} = 'tag name';            $self->{state} = TAG_NAME_STATE;
393            !!!next-input-character;            !!!next-input-character;
394            redo A;            redo A;
395          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_input_character} == 0x003E) { # >
396            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag');
397            $self->{state} = 'data';            $self->{state} = DATA_STATE;
398            !!!next-input-character;            !!!next-input-character;
399    
400            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});
401    
402            redo A;            redo A;
403          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_input_character} == 0x003F) { # ?
404            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio');
405            $self->{state} = 'bogus comment';            $self->{state} = BOGUS_COMMENT_STATE;
406            ## $self->{next_input_character} is intentionally left as is            ## $self->{next_input_character} is intentionally left as is
407            redo A;            redo A;
408          } else {          } else {
409            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago');
410            $self->{state} = 'data';            $self->{state} = DATA_STATE;
411            ## reconsume            ## reconsume
412    
413            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<'});
414    
415            redo A;            redo A;
416          }          }
417        } else {        } else {
418          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
419        }        }
420      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
421        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
           $self->{content_model_flag} eq 'CDATA') {  
422          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_emitted_start_tag_name}) {
423            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
424            my @next_char;            my @next_char;
# Line 345  sub _get_next_token ($) { Line 432  sub _get_next_token ($) {
432              } else {              } else {
433                $self->{next_input_character} = shift @next_char; # reconsume                $self->{next_input_character} = shift @next_char; # reconsume
434                !!!back-next-input-character (@next_char);                !!!back-next-input-character (@next_char);
435                $self->{state} = 'data';                $self->{state} = DATA_STATE;
436    
437                !!!emit ({type => 'character', data => '</'});                !!!emit ({type => CHARACTER_TOKEN, data => '</'});
438        
439                redo A;                redo A;
440              }              }
# Line 364  sub _get_next_token ($) { Line 451  sub _get_next_token ($) {
451                    $self->{next_input_character} == -1) {                    $self->{next_input_character} == -1) {
452              $self->{next_input_character} = shift @next_char; # reconsume              $self->{next_input_character} = shift @next_char; # reconsume
453              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
454              $self->{state} = 'data';              $self->{state} = DATA_STATE;
455              !!!emit ({type => 'character', data => '</'});              !!!emit ({type => CHARACTER_TOKEN, data => '</'});
456              redo A;              redo A;
457            } else {            } else {
458              $self->{next_input_character} = shift @next_char;              $self->{next_input_character} = shift @next_char;
# Line 375  sub _get_next_token ($) { Line 462  sub _get_next_token ($) {
462          } else {          } else {
463            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
464            # next-input-character is already done            # next-input-character is already done
465            $self->{state} = 'data';            $self->{state} = DATA_STATE;
466            !!!emit ({type => 'character', data => '</'});            !!!emit ({type => CHARACTER_TOKEN, data => '</'});
467            redo A;            redo A;
468          }          }
469        }        }
470                
471        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{next_input_character} and
472            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{next_input_character} <= 0x005A) { # A..Z
473          $self->{current_token} = {type => 'end tag',          $self->{current_token} = {type => END_TAG_TOKEN,
474                            tag_name => chr ($self->{next_input_character} + 0x0020)};                            tag_name => chr ($self->{next_input_character} + 0x0020)};
475          $self->{state} = 'tag name';          $self->{state} = TAG_NAME_STATE;
476          !!!next-input-character;          !!!next-input-character;
477          redo A;          redo A;
478        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{next_input_character} and
479                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{next_input_character} <= 0x007A) { # a..z
480          $self->{current_token} = {type => 'end tag',          $self->{current_token} = {type => END_TAG_TOKEN,
481                            tag_name => chr ($self->{next_input_character})};                            tag_name => chr ($self->{next_input_character})};
482          $self->{state} = 'tag name';          $self->{state} = TAG_NAME_STATE;
483          !!!next-input-character;          !!!next-input-character;
484          redo A;          redo A;
485        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
486          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag');
487          $self->{state} = 'data';          $self->{state} = DATA_STATE;
488          !!!next-input-character;          !!!next-input-character;
489          redo A;          redo A;
490        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
491          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
492          $self->{state} = 'data';          $self->{state} = DATA_STATE;
493          # reconsume          # reconsume
494    
495          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</'});
496    
497          redo A;          redo A;
498        } else {        } else {
499          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
500          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
501          ## $self->{next_input_character} is intentionally left as is          ## $self->{next_input_character} is intentionally left as is
502          redo A;          redo A;
503        }        }
504      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
505        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
506            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
507            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
508            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
509            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
510          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
511          !!!next-input-character;          !!!next-input-character;
512          redo A;          redo A;
513        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
514          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
515            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
516                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
517            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
518          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
519            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
520            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
521              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
522            }            }
523          } else {          } else {
524            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
525          }          }
526          $self->{state} = 'data';          $self->{state} = DATA_STATE;
527          !!!next-input-character;          !!!next-input-character;
528    
529          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 451  sub _get_next_token ($) { Line 538  sub _get_next_token ($) {
538          redo A;          redo A;
539        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
540          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
541          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
542            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
543                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
544            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
545          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
546            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
547            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
548              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
549            }            }
550          } else {          } else {
551            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
552          }          }
553          $self->{state} = 'data';          $self->{state} = DATA_STATE;
554          # reconsume          # reconsume
555    
556          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 472  sub _get_next_token ($) { Line 559  sub _get_next_token ($) {
559        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
560          !!!next-input-character;          !!!next-input-character;
561          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
562              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
563              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
564            # permitted slash            # permitted slash
565            #            #
566          } else {          } else {
567            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
568          }          }
569          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
570          # next-input-character is already done          # next-input-character is already done
571          redo A;          redo A;
572        } else {        } else {
# Line 489  sub _get_next_token ($) { Line 576  sub _get_next_token ($) {
576          !!!next-input-character;          !!!next-input-character;
577          redo A;          redo A;
578        }        }
579      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
580        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
581            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
582            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 499  sub _get_next_token ($) { Line 586  sub _get_next_token ($) {
586          !!!next-input-character;          !!!next-input-character;
587          redo A;          redo A;
588        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
589          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
590            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
591                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
592            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
593          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
594            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
595            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
596              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
597            }            }
598          } else {          } else {
599            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
600          }          }
601          $self->{state} = 'data';          $self->{state} = DATA_STATE;
602          !!!next-input-character;          !!!next-input-character;
603    
604          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 521  sub _get_next_token ($) { Line 608  sub _get_next_token ($) {
608                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_input_character} <= 0x005A) { # A..Z
609          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),
610                                value => ''};                                value => ''};
611          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
612          !!!next-input-character;          !!!next-input-character;
613          redo A;          redo A;
614        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
615          !!!next-input-character;          !!!next-input-character;
616          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
617              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
618              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
619            # permitted slash            # permitted slash
620            #            #
# Line 539  sub _get_next_token ($) { Line 626  sub _get_next_token ($) {
626          redo A;          redo A;
627        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
628          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
629          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
630            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
631                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
632            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
633          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
634            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
635            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
637            }            }
638          } else {          } else {
639            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
640          }          }
641          $self->{state} = 'data';          $self->{state} = DATA_STATE;
642          # reconsume          # reconsume
643    
644          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 560  sub _get_next_token ($) { Line 647  sub _get_next_token ($) {
647        } else {        } else {
648          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{current_attribute} = {name => chr ($self->{next_input_character}),
649                                value => ''};                                value => ''};
650          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
651          !!!next-input-character;          !!!next-input-character;
652          redo A;          redo A;
653        }        }
654      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
655        my $before_leave = sub {        my $before_leave = sub {
656          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
657              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
658            !!!parse-error (type => 'dupulicate attribute');            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});
659            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
660          } else {          } else {
661            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
# Line 582  sub _get_next_token ($) { Line 669  sub _get_next_token ($) {
669            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
670            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
671          $before_leave->();          $before_leave->();
672          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
673          !!!next-input-character;          !!!next-input-character;
674          redo A;          redo A;
675        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_input_character} == 0x003D) { # =
676          $before_leave->();          $before_leave->();
677          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
678          !!!next-input-character;          !!!next-input-character;
679          redo A;          redo A;
680        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
681          $before_leave->();          $before_leave->();
682          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
683            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
684                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
685            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
686          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
687            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
688            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
689              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
690            }            }
691          } else {          } else {
692            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
693          }          }
694          $self->{state} = 'data';          $self->{state} = DATA_STATE;
695          !!!next-input-character;          !!!next-input-character;
696    
697          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 620  sub _get_next_token ($) { Line 707  sub _get_next_token ($) {
707          $before_leave->();          $before_leave->();
708          !!!next-input-character;          !!!next-input-character;
709          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
710              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
711              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
712            # permitted slash            # permitted slash
713            #            #
714          } else {          } else {
715            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
716          }          }
717          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
718          # next-input-character is already done          # next-input-character is already done
719          redo A;          redo A;
720        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
721          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
722          $before_leave->();          $before_leave->();
723          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
724            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
725                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
726            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
727          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
728            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
729            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
730              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
731            }            }
732          } else {          } else {
733            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
734          }          }
735          $self->{state} = 'data';          $self->{state} = DATA_STATE;
736          # reconsume          # reconsume
737    
738          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 657  sub _get_next_token ($) { Line 744  sub _get_next_token ($) {
744          !!!next-input-character;          !!!next-input-character;
745          redo A;          redo A;
746        }        }
747      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
748        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
749            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
750            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 667  sub _get_next_token ($) { Line 754  sub _get_next_token ($) {
754          !!!next-input-character;          !!!next-input-character;
755          redo A;          redo A;
756        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_input_character} == 0x003D) { # =
757          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
758          !!!next-input-character;          !!!next-input-character;
759          redo A;          redo A;
760        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
761          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
762            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
763                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
764            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
765          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
766            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
767            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
768              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
769            }            }
770          } else {          } else {
771            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
772          }          }
773          $self->{state} = 'data';          $self->{state} = DATA_STATE;
774          !!!next-input-character;          !!!next-input-character;
775    
776          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 693  sub _get_next_token ($) { Line 780  sub _get_next_token ($) {
780                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_input_character} <= 0x005A) { # A..Z
781          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),
782                                value => ''};                                value => ''};
783          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
784          !!!next-input-character;          !!!next-input-character;
785          redo A;          redo A;
786        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
787          !!!next-input-character;          !!!next-input-character;
788          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
789              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
790              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
791            # permitted slash            # permitted slash
792            #            #
793          } else {          } else {
794            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
795              ## TODO: Different error type for <aa / bb> than <aa/>
796          }          }
797          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
798          # next-input-character is already done          # next-input-character is already done
799          redo A;          redo A;
800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
801          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
802          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
803            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
804                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
805            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
806          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
807            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
808            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
809              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
810            }            }
811          } else {          } else {
812            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
813          }          }
814          $self->{state} = 'data';          $self->{state} = DATA_STATE;
815          # reconsume          # reconsume
816    
817          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 732  sub _get_next_token ($) { Line 820  sub _get_next_token ($) {
820        } else {        } else {
821          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{current_attribute} = {name => chr ($self->{next_input_character}),
822                                value => ''};                                value => ''};
823          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
824          !!!next-input-character;          !!!next-input-character;
825          redo A;                  redo A;        
826        }        }
827      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
828        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
829            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
830            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 746  sub _get_next_token ($) { Line 834  sub _get_next_token ($) {
834          !!!next-input-character;          !!!next-input-character;
835          redo A;          redo A;
836        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
837          $self->{state} = 'attribute value (double-quoted)';          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
838          !!!next-input-character;          !!!next-input-character;
839          redo A;          redo A;
840        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
841          $self->{state} = 'attribute value (unquoted)';          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
842          ## reconsume          ## reconsume
843          redo A;          redo A;
844        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
845          $self->{state} = 'attribute value (single-quoted)';          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
846          !!!next-input-character;          !!!next-input-character;
847          redo A;          redo A;
848        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
849          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
850            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
851                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
852            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
853          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
854            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
855            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
856              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
857            }            }
858          } else {          } else {
859            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
860          }          }
861          $self->{state} = 'data';          $self->{state} = DATA_STATE;
862          !!!next-input-character;          !!!next-input-character;
863    
864          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 778  sub _get_next_token ($) { Line 866  sub _get_next_token ($) {
866          redo A;          redo A;
867        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
868          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
869          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
870            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
871                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
872            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
873          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
874            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
875            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
876              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
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          ## reconsume          ## reconsume
883    
884          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 798  sub _get_next_token ($) { Line 886  sub _get_next_token ($) {
886          redo A;          redo A;
887        } else {        } else {
888          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});
889          $self->{state} = 'attribute value (unquoted)';          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
890          !!!next-input-character;          !!!next-input-character;
891          redo A;          redo A;
892        }        }
893      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
894        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
895          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
896          !!!next-input-character;          !!!next-input-character;
897          redo A;          redo A;
898        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
899          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          $self->{last_attribute_value_state} = $self->{state};
900          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
901          !!!next-input-character;          !!!next-input-character;
902          redo A;          redo A;
903        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
904          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
905          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
906            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
907                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
908            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
909          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
910            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
911            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
912              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
913            }            }
914          } else {          } else {
915            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
916          }          }
917          $self->{state} = 'data';          $self->{state} = DATA_STATE;
918          ## reconsume          ## reconsume
919    
920          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 838  sub _get_next_token ($) { Line 926  sub _get_next_token ($) {
926          !!!next-input-character;          !!!next-input-character;
927          redo A;          redo A;
928        }        }
929      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
930        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
931          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
932          !!!next-input-character;          !!!next-input-character;
933          redo A;          redo A;
934        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
935          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';          $self->{last_attribute_value_state} = $self->{state};
936          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
937          !!!next-input-character;          !!!next-input-character;
938          redo A;          redo A;
939        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
940          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
941          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
942            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
943                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
944            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
945          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
946            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
947            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
949            }            }
950          } else {          } else {
951            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
952          }          }
953          $self->{state} = 'data';          $self->{state} = DATA_STATE;
954          ## reconsume          ## reconsume
955    
956          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 874  sub _get_next_token ($) { Line 962  sub _get_next_token ($) {
962          !!!next-input-character;          !!!next-input-character;
963          redo A;          redo A;
964        }        }
965      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
966        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
967            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
968            $self->{next_input_character} == 0x000B or # HT            $self->{next_input_character} == 0x000B or # HT
969            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
970            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
971          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
972          !!!next-input-character;          !!!next-input-character;
973          redo A;          redo A;
974        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
975          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          $self->{last_attribute_value_state} = $self->{state};
976          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
977          !!!next-input-character;          !!!next-input-character;
978          redo A;          redo A;
979        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
980          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
981            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
982                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
983            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
984          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
985            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
986            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
987              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
988            }            }
989          } else {          } else {
990            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
991          }          }
992          $self->{state} = 'data';          $self->{state} = DATA_STATE;
993          !!!next-input-character;          !!!next-input-character;
994    
995          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 909  sub _get_next_token ($) { Line 997  sub _get_next_token ($) {
997          redo A;          redo A;
998        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
999          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1000          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1001            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
1002                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
1003            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1004          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1005            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1006            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1007              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1008            }            }
1009          } else {          } else {
1010            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1011          }          }
1012          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1013          ## reconsume          ## reconsume
1014    
1015          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 933  sub _get_next_token ($) { Line 1021  sub _get_next_token ($) {
1021          !!!next-input-character;          !!!next-input-character;
1022          redo A;          redo A;
1023        }        }
1024      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {
1025        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);
1026    
1027        unless (defined $token) {        unless (defined $token) {
# Line 946  sub _get_next_token ($) { Line 1034  sub _get_next_token ($) {
1034        $self->{state} = $self->{last_attribute_value_state};        $self->{state} = $self->{last_attribute_value_state};
1035        # next-input-character is already done        # next-input-character is already done
1036        redo A;        redo A;
1037      } elsif ($self->{state} eq 'bogus comment') {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1038        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1039                
1040        my $token = {type => 'comment', data => ''};        my $token = {type => COMMENT_TOKEN, data => ''};
1041    
1042        BC: {        BC: {
1043          if ($self->{next_input_character} == 0x003E) { # >          if ($self->{next_input_character} == 0x003E) { # >
1044            $self->{state} = 'data';            $self->{state} = DATA_STATE;
1045            !!!next-input-character;            !!!next-input-character;
1046    
1047            !!!emit ($token);            !!!emit ($token);
1048    
1049            redo A;            redo A;
1050          } elsif ($self->{next_input_character} == -1) {          } elsif ($self->{next_input_character} == -1) {
1051            $self->{state} = 'data';            $self->{state} = DATA_STATE;
1052            ## reconsume            ## reconsume
1053    
1054            !!!emit ($token);            !!!emit ($token);
# Line 972  sub _get_next_token ($) { Line 1060  sub _get_next_token ($) {
1060            redo BC;            redo BC;
1061          }          }
1062        } # BC        } # BC
1063      } elsif ($self->{state} eq 'markup declaration open') {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1064        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1065    
1066        my @next_char;        my @next_char;
# Line 982  sub _get_next_token ($) { Line 1070  sub _get_next_token ($) {
1070          !!!next-input-character;          !!!next-input-character;
1071          push @next_char, $self->{next_input_character};          push @next_char, $self->{next_input_character};
1072          if ($self->{next_input_character} == 0x002D) { # -          if ($self->{next_input_character} == 0x002D) { # -
1073            $self->{current_token} = {type => 'comment', data => ''};            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};
1074            $self->{state} = 'comment start';            $self->{state} = COMMENT_START_STATE;
1075            !!!next-input-character;            !!!next-input-character;
1076            redo A;            redo A;
1077          }          }
# Line 1014  sub _get_next_token ($) { Line 1102  sub _get_next_token ($) {
1102                    if ($self->{next_input_character} == 0x0045 or # E                    if ($self->{next_input_character} == 0x0045 or # E
1103                        $self->{next_input_character} == 0x0065) { # e                        $self->{next_input_character} == 0x0065) { # e
1104                      ## ISSUE: What a stupid code this is!                      ## ISSUE: What a stupid code this is!
1105                      $self->{state} = 'DOCTYPE';                      $self->{state} = DOCTYPE_STATE;
1106                      !!!next-input-character;                      !!!next-input-character;
1107                      redo A;                      redo A;
1108                    }                    }
# Line 1028  sub _get_next_token ($) { Line 1116  sub _get_next_token ($) {
1116        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment');
1117        $self->{next_input_character} = shift @next_char;        $self->{next_input_character} = shift @next_char;
1118        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1119        $self->{state} = 'bogus comment';        $self->{state} = BOGUS_COMMENT_STATE;
1120        redo A;        redo A;
1121                
1122        ## ISSUE: typos in spec: chacacters, is is a parse error        ## ISSUE: typos in spec: chacacters, is is a parse error
1123        ## 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?
1124      } elsif ($self->{state} eq 'comment start') {      } elsif ($self->{state} == COMMENT_START_STATE) {
1125        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1126          $self->{state} = 'comment start dash';          $self->{state} = COMMENT_START_DASH_STATE;
1127          !!!next-input-character;          !!!next-input-character;
1128          redo A;          redo A;
1129        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1130          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1131          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1132          !!!next-input-character;          !!!next-input-character;
1133    
1134          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1048  sub _get_next_token ($) { Line 1136  sub _get_next_token ($) {
1136          redo A;          redo A;
1137        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1138          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1139          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1140          ## reconsume          ## reconsume
1141    
1142          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1057  sub _get_next_token ($) { Line 1145  sub _get_next_token ($) {
1145        } else {        } else {
1146          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1147              .= chr ($self->{next_input_character});              .= chr ($self->{next_input_character});
1148          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1149          !!!next-input-character;          !!!next-input-character;
1150          redo A;          redo A;
1151        }        }
1152      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
1153        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1154          $self->{state} = 'comment end';          $self->{state} = COMMENT_END_STATE;
1155          !!!next-input-character;          !!!next-input-character;
1156          redo A;          redo A;
1157        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1158          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1159          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1160          !!!next-input-character;          !!!next-input-character;
1161    
1162          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1076  sub _get_next_token ($) { Line 1164  sub _get_next_token ($) {
1164          redo A;          redo A;
1165        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1166          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1167          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1168          ## reconsume          ## reconsume
1169    
1170          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1084  sub _get_next_token ($) { Line 1172  sub _get_next_token ($) {
1172          redo A;          redo A;
1173        } else {        } else {
1174          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1175              .= chr ($self->{next_input_character});              .= '-' . chr ($self->{next_input_character});
1176          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1177          !!!next-input-character;          !!!next-input-character;
1178          redo A;          redo A;
1179        }        }
1180      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
1181        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1182          $self->{state} = 'comment end dash';          $self->{state} = COMMENT_END_DASH_STATE;
1183          !!!next-input-character;          !!!next-input-character;
1184          redo A;          redo A;
1185        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1186          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1187          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1188          ## reconsume          ## reconsume
1189    
1190          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1108  sub _get_next_token ($) { Line 1196  sub _get_next_token ($) {
1196          !!!next-input-character;          !!!next-input-character;
1197          redo A;          redo A;
1198        }        }
1199      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
1200        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1201          $self->{state} = 'comment end';          $self->{state} = COMMENT_END_STATE;
1202          !!!next-input-character;          !!!next-input-character;
1203          redo A;          redo A;
1204        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1205          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1206          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1207          ## reconsume          ## reconsume
1208    
1209          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1123  sub _get_next_token ($) { Line 1211  sub _get_next_token ($) {
1211          redo A;          redo A;
1212        } else {        } else {
1213          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment
1214          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1215          !!!next-input-character;          !!!next-input-character;
1216          redo A;          redo A;
1217        }        }
1218      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
1219        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_input_character} == 0x003E) { # >
1220          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1221          !!!next-input-character;          !!!next-input-character;
1222    
1223          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1143  sub _get_next_token ($) { Line 1231  sub _get_next_token ($) {
1231          redo A;          redo A;
1232        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1233          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1234          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1235          ## reconsume          ## reconsume
1236    
1237          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1152  sub _get_next_token ($) { Line 1240  sub _get_next_token ($) {
1240        } else {        } else {
1241          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment');
1242          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment
1243          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1244          !!!next-input-character;          !!!next-input-character;
1245          redo A;          redo A;
1246        }        }
1247      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
1248        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1249            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1250            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
1251            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
1252            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
1253          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1254          !!!next-input-character;          !!!next-input-character;
1255          redo A;          redo A;
1256        } else {        } else {
1257          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
1258          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1259          ## reconsume          ## reconsume
1260          redo A;          redo A;
1261        }        }
1262      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
1263        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1264            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1265            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 1182  sub _get_next_token ($) { Line 1270  sub _get_next_token ($) {
1270          redo A;          redo A;
1271        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1272          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1273          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1274          !!!next-input-character;          !!!next-input-character;
1275    
1276          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect
1277    
1278          redo A;          redo A;
1279        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1280          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1281          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1282          ## reconsume          ## reconsume
1283    
1284          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect
1285    
1286          redo A;          redo A;
1287        } else {        } else {
1288          $self->{current_token}          $self->{current_token}
1289              = {type => 'DOCTYPE',              = {type => DOCTYPE_TOKEN,
1290                 name => chr ($self->{next_input_character}),                 name => chr ($self->{next_input_character}),
1291                 correct => 1};                 correct => 1};
1292  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
1293          $self->{state} = 'DOCTYPE name';          $self->{state} = DOCTYPE_NAME_STATE;
1294          !!!next-input-character;          !!!next-input-character;
1295          redo A;          redo A;
1296        }        }
1297      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
1298  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
1299        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1300            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1301            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
1302            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
1303            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
1304          $self->{state} = 'after DOCTYPE name';          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
1305          !!!next-input-character;          !!!next-input-character;
1306          redo A;          redo A;
1307        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1308          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1309          !!!next-input-character;          !!!next-input-character;
1310    
1311          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1225  sub _get_next_token ($) { Line 1313  sub _get_next_token ($) {
1313          redo A;          redo A;
1314        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1315          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1316          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1317          ## reconsume          ## reconsume
1318    
1319          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1239  sub _get_next_token ($) { Line 1327  sub _get_next_token ($) {
1327          !!!next-input-character;          !!!next-input-character;
1328          redo A;          redo A;
1329        }        }
1330      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
1331        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1332            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1333            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 1249  sub _get_next_token ($) { Line 1337  sub _get_next_token ($) {
1337          !!!next-input-character;          !!!next-input-character;
1338          redo A;          redo A;
1339        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1340          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1341          !!!next-input-character;          !!!next-input-character;
1342    
1343          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1257  sub _get_next_token ($) { Line 1345  sub _get_next_token ($) {
1345          redo A;          redo A;
1346        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1347          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1348          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1349          ## reconsume          ## reconsume
1350    
1351          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1281  sub _get_next_token ($) { Line 1369  sub _get_next_token ($) {
1369                  !!!next-input-character;                  !!!next-input-character;
1370                  if ($self->{next_input_character} == 0x0043 or # C                  if ($self->{next_input_character} == 0x0043 or # C
1371                      $self->{next_input_character} == 0x0063) { # c                      $self->{next_input_character} == 0x0063) { # c
1372                    $self->{state} = 'before DOCTYPE public identifier';                    $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1373                    !!!next-input-character;                    !!!next-input-character;
1374                    redo A;                    redo A;
1375                  }                  }
# Line 1308  sub _get_next_token ($) { Line 1396  sub _get_next_token ($) {
1396                  !!!next-input-character;                  !!!next-input-character;
1397                  if ($self->{next_input_character} == 0x004D or # M                  if ($self->{next_input_character} == 0x004D or # M
1398                      $self->{next_input_character} == 0x006D) { # m                      $self->{next_input_character} == 0x006D) { # m
1399                    $self->{state} = 'before DOCTYPE system identifier';                    $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1400                    !!!next-input-character;                    !!!next-input-character;
1401                    redo A;                    redo A;
1402                  }                  }
# Line 1324  sub _get_next_token ($) { Line 1412  sub _get_next_token ($) {
1412        }        }
1413    
1414        !!!parse-error (type => 'string after DOCTYPE name');        !!!parse-error (type => 'string after DOCTYPE name');
1415        $self->{state} = 'bogus DOCTYPE';        $self->{state} = BOGUS_DOCTYPE_STATE;
1416        # next-input-character is already done        # next-input-character is already done
1417        redo A;        redo A;
1418      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
1419        if ({        if ({
1420              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1421              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1337  sub _get_next_token ($) { Line 1425  sub _get_next_token ($) {
1425          redo A;          redo A;
1426        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{next_input_character} eq 0x0022) { # "
1427          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1428          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
1429          !!!next-input-character;          !!!next-input-character;
1430          redo A;          redo A;
1431        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{next_input_character} eq 0x0027) { # '
1432          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1433          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
1434          !!!next-input-character;          !!!next-input-character;
1435          redo A;          redo A;
1436        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{next_input_character} eq 0x003E) { # >
1437          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
1438    
1439          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1440          !!!next-input-character;          !!!next-input-character;
1441    
1442          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1358  sub _get_next_token ($) { Line 1446  sub _get_next_token ($) {
1446        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1447          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1448    
1449          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1450          ## reconsume          ## reconsume
1451    
1452          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1367  sub _get_next_token ($) { Line 1455  sub _get_next_token ($) {
1455          redo A;          redo A;
1456        } else {        } else {
1457          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
1458          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1459          !!!next-input-character;          !!!next-input-character;
1460          redo A;          redo A;
1461        }        }
1462      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
1463        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
1464          $self->{state} = 'after DOCTYPE public identifier';          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1465          !!!next-input-character;          !!!next-input-character;
1466          redo A;          redo A;
1467        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1468          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
1469    
1470          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1471          ## reconsume          ## reconsume
1472    
1473          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1393  sub _get_next_token ($) { Line 1481  sub _get_next_token ($) {
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        }        }
1484      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
1485        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
1486          $self->{state} = 'after DOCTYPE public identifier';          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1487          !!!next-input-character;          !!!next-input-character;
1488          redo A;          redo A;
1489        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1490          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
1491    
1492          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1493          ## reconsume          ## reconsume
1494    
1495          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1415  sub _get_next_token ($) { Line 1503  sub _get_next_token ($) {
1503          !!!next-input-character;          !!!next-input-character;
1504          redo A;          redo A;
1505        }        }
1506      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
1507        if ({        if ({
1508              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1509              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1425  sub _get_next_token ($) { Line 1513  sub _get_next_token ($) {
1513          redo A;          redo A;
1514        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
1515          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1516          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
1517          !!!next-input-character;          !!!next-input-character;
1518          redo A;          redo A;
1519        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
1520          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1521          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
1522          !!!next-input-character;          !!!next-input-character;
1523          redo A;          redo A;
1524        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1525          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1526          !!!next-input-character;          !!!next-input-character;
1527    
1528          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1443  sub _get_next_token ($) { Line 1531  sub _get_next_token ($) {
1531        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1532          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1533    
1534          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1535          ## reconsume          ## reconsume
1536    
1537          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1452  sub _get_next_token ($) { Line 1540  sub _get_next_token ($) {
1540          redo A;          redo A;
1541        } else {        } else {
1542          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
1543          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1544          !!!next-input-character;          !!!next-input-character;
1545          redo A;          redo A;
1546        }        }
1547      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
1548        if ({        if ({
1549              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1550              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1466  sub _get_next_token ($) { Line 1554  sub _get_next_token ($) {
1554          redo A;          redo A;
1555        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
1556          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1557          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
1558          !!!next-input-character;          !!!next-input-character;
1559          redo A;          redo A;
1560        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
1561          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1562          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
1563          !!!next-input-character;          !!!next-input-character;
1564          redo A;          redo A;
1565        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1566          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
1567          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1568          !!!next-input-character;          !!!next-input-character;
1569    
1570          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1486  sub _get_next_token ($) { Line 1574  sub _get_next_token ($) {
1574        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1575          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1576    
1577          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1578          ## reconsume          ## reconsume
1579    
1580          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1495  sub _get_next_token ($) { Line 1583  sub _get_next_token ($) {
1583          redo A;          redo A;
1584        } else {        } else {
1585          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
1586          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1587          !!!next-input-character;          !!!next-input-character;
1588          redo A;          redo A;
1589        }        }
1590      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
1591        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
1592          $self->{state} = 'after DOCTYPE system identifier';          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1593          !!!next-input-character;          !!!next-input-character;
1594          redo A;          redo A;
1595        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1596          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
1597    
1598          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1599          ## reconsume          ## reconsume
1600    
1601          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1521  sub _get_next_token ($) { Line 1609  sub _get_next_token ($) {
1609          !!!next-input-character;          !!!next-input-character;
1610          redo A;          redo A;
1611        }        }
1612      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
1613        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
1614          $self->{state} = 'after DOCTYPE system identifier';          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1615          !!!next-input-character;          !!!next-input-character;
1616          redo A;          redo A;
1617        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1618          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
1619    
1620          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1621          ## reconsume          ## reconsume
1622    
1623          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1543  sub _get_next_token ($) { Line 1631  sub _get_next_token ($) {
1631          !!!next-input-character;          !!!next-input-character;
1632          redo A;          redo A;
1633        }        }
1634      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
1635        if ({        if ({
1636              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1637              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1552  sub _get_next_token ($) { Line 1640  sub _get_next_token ($) {
1640          !!!next-input-character;          !!!next-input-character;
1641          redo A;          redo A;
1642        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1643          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1644          !!!next-input-character;          !!!next-input-character;
1645    
1646          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1561  sub _get_next_token ($) { Line 1649  sub _get_next_token ($) {
1649        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1650          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1651    
1652          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1653          ## reconsume          ## reconsume
1654    
1655          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1570  sub _get_next_token ($) { Line 1658  sub _get_next_token ($) {
1658          redo A;          redo A;
1659        } else {        } else {
1660          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
1661          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1662          !!!next-input-character;          !!!next-input-character;
1663          redo A;          redo A;
1664        }        }
1665      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
1666        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_input_character} == 0x003E) { # >
1667          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1668          !!!next-input-character;          !!!next-input-character;
1669    
1670          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1585  sub _get_next_token ($) { Line 1673  sub _get_next_token ($) {
1673          redo A;          redo A;
1674        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1675          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1676          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1677          ## reconsume          ## reconsume
1678    
1679          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1643  sub _tokenize_attempt_to_consume_an_enti Line 1731  sub _tokenize_attempt_to_consume_an_enti
1731            redo X;            redo X;
1732          } elsif (not defined $code) { # no hexadecimal digit          } elsif (not defined $code) { # no hexadecimal digit
1733            !!!parse-error (type => 'bare hcro');            !!!parse-error (type => 'bare hcro');
1734              !!!back-next-input-character ($x_char, $self->{next_input_character});
1735            $self->{next_input_character} = 0x0023; # #            $self->{next_input_character} = 0x0023; # #
           !!!back-next-input-character ($x_char);  
1736            return undef;            return undef;
1737          } elsif ($self->{next_input_character} == 0x003B) { # ;          } elsif ($self->{next_input_character} == 0x003B) { # ;
1738            !!!next-input-character;            !!!next-input-character;
# Line 1666  sub _tokenize_attempt_to_consume_an_enti Line 1754  sub _tokenize_attempt_to_consume_an_enti
1754            $code = $c1_entity_char->{$code};            $code = $c1_entity_char->{$code};
1755          }          }
1756    
1757          return {type => 'character', data => chr $code};          return {type => CHARACTER_TOKEN, data => chr $code};
1758        } # X        } # X
1759      } elsif (0x0030 <= $self->{next_input_character} and      } elsif (0x0030 <= $self->{next_input_character} and
1760               $self->{next_input_character} <= 0x0039) { # 0..9               $self->{next_input_character} <= 0x0039) { # 0..9
# Line 1701  sub _tokenize_attempt_to_consume_an_enti Line 1789  sub _tokenize_attempt_to_consume_an_enti
1789          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
1790        }        }
1791                
1792        return {type => 'character', data => chr $code};        return {type => CHARACTER_TOKEN, data => chr $code};
1793      } else {      } else {
1794        !!!parse-error (type => 'bare nero');        !!!parse-error (type => 'bare nero');
1795        !!!back-next-input-character ($self->{next_input_character});        !!!back-next-input-character ($self->{next_input_character});
# Line 1716  sub _tokenize_attempt_to_consume_an_enti Line 1804  sub _tokenize_attempt_to_consume_an_enti
1804      !!!next-input-character;      !!!next-input-character;
1805    
1806      my $value = $entity_name;      my $value = $entity_name;
1807      my $match;      my $match = 0;
1808      require Whatpm::_NamedEntityList;      require Whatpm::_NamedEntityList;
1809      our $EntityChar;      our $EntityChar;
1810    
# Line 1736  sub _tokenize_attempt_to_consume_an_enti Line 1824  sub _tokenize_attempt_to_consume_an_enti
1824            $match = 1;            $match = 1;
1825            !!!next-input-character;            !!!next-input-character;
1826            last;            last;
1827          } elsif (not $in_attr) {          } else {
1828            $value = $EntityChar->{$entity_name};            $value = $EntityChar->{$entity_name};
1829            $match = -1;            $match = -1;
1830          } else {            !!!next-input-character;
           $value .= chr $self->{next_input_character};  
1831          }          }
1832        } else {        } else {
1833          $value .= chr $self->{next_input_character};          $value .= chr $self->{next_input_character};
1834            $match *= 2;
1835            !!!next-input-character;
1836        }        }
       !!!next-input-character;  
1837      }      }
1838            
1839      if ($match > 0) {      if ($match > 0) {
1840        return {type => 'character', data => $value};        return {type => CHARACTER_TOKEN, data => $value};
1841      } elsif ($match < 0) {      } elsif ($match < 0) {
1842        !!!parse-error (type => 'no refc');        !!!parse-error (type => 'no refc');
1843        return {type => 'character', data => $value};        if ($in_attr and $match < -1) {
1844            return {type => CHARACTER_TOKEN, data => '&'.$entity_name};
1845          } else {
1846            return {type => CHARACTER_TOKEN, data => $value};
1847          }
1848      } else {      } else {
1849        !!!parse-error (type => 'bare ero');        !!!parse-error (type => 'bare ero');
1850        ## NOTE: No characters are consumed in the spec.        ## NOTE: No characters are consumed in the spec.
1851        return {type => 'character', data => '&'.$value};        return {type => CHARACTER_TOKEN, data => '&'.$value};
1852      }      }
1853    } else {    } else {
1854      ## no characters are consumed      ## no characters are consumed
# Line 1798  sub _construct_tree ($) { Line 1890  sub _construct_tree ($) {
1890        
1891    !!!next-token;    !!!next-token;
1892    
1893    $self->{insertion_mode} = 'before head';    $self->{insertion_mode} = BEFORE_HEAD_IM;
1894    undef $self->{form_element};    undef $self->{form_element};
1895    undef $self->{head_element};    undef $self->{head_element};
1896    $self->{open_elements} = [];    $self->{open_elements} = [];
# Line 1812  sub _construct_tree ($) { Line 1904  sub _construct_tree ($) {
1904  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
1905    my $self = shift;    my $self = shift;
1906    INITIAL: {    INITIAL: {
1907      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
1908        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
1909        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
1910        ## language.        ## language.
# Line 1939  sub _tree_construction_initial ($) { Line 2031  sub _tree_construction_initial ($) {
2031        !!!next-token;        !!!next-token;
2032        return;        return;
2033      } elsif ({      } elsif ({
2034                'start tag' => 1,                START_TAG_TOKEN, 1,
2035                'end tag' => 1,                END_TAG_TOKEN, 1,
2036                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
2037               }->{$token->{type}}) {               }->{$token->{type}}) {
2038        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE');
2039        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2040        ## Go to the root element phase        ## Go to the root element phase
2041        ## reprocess        ## reprocess
2042        return;        return;
2043      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
2044        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2045          ## Ignore the token          ## Ignore the token
2046    
# Line 1964  sub _tree_construction_initial ($) { Line 2056  sub _tree_construction_initial ($) {
2056        ## Go to the root element phase        ## Go to the root element phase
2057        ## reprocess        ## reprocess
2058        return;        return;
2059      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
2060        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
2061        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
2062                
# Line 1972  sub _tree_construction_initial ($) { Line 2064  sub _tree_construction_initial ($) {
2064        !!!next-token;        !!!next-token;
2065        redo INITIAL;        redo INITIAL;
2066      } else {      } else {
2067        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
2068      }      }
2069    } # INITIAL    } # INITIAL
2070  } # _tree_construction_initial  } # _tree_construction_initial
# Line 1981  sub _tree_construction_root_element ($) Line 2073  sub _tree_construction_root_element ($)
2073    my $self = shift;    my $self = shift;
2074        
2075    B: {    B: {
2076        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
2077          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE');
2078          ## Ignore the token          ## Ignore the token
2079          ## Stay in the phase          ## Stay in the phase
2080          !!!next-token;          !!!next-token;
2081          redo B;          redo B;
2082        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
2083          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
2084          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
2085          ## Stay in the phase          ## Stay in the phase
2086          !!!next-token;          !!!next-token;
2087          redo B;          redo B;
2088        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
2089          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2090            ## Ignore the token.            ## Ignore the token.
2091    
# Line 2005  sub _tree_construction_root_element ($) Line 2097  sub _tree_construction_root_element ($)
2097          }          }
2098          #          #
2099        } elsif ({        } elsif ({
2100                  'start tag' => 1,                  START_TAG_TOKEN, 1,
2101                  'end tag' => 1,                  END_TAG_TOKEN, 1,
2102                  'end-of-file' => 1,                  END_OF_FILE_TOKEN, 1,
2103                 }->{$token->{type}}) {                 }->{$token->{type}}) {
2104          ## ISSUE: There is an issue in the spec          ## ISSUE: There is an issue in the spec
2105          #          #
2106        } else {        } else {
2107          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
2108        }        }
2109        my $root_element; !!!create-element ($root_element, 'html');        my $root_element; !!!create-element ($root_element, 'html');
2110        $self->{document}->append_child ($root_element);        $self->{document}->append_child ($root_element);
2111        push @{$self->{open_elements}}, [$root_element, 'html'];        push @{$self->{open_elements}}, [$root_element, 'html'];
       #$phase = 'main';  
2112        ## reprocess        ## reprocess
2113        #redo B;        #redo B;
2114        return;        return; ## Go to the main phase.
2115    } # B    } # B
2116  } # _tree_construction_root_element  } # _tree_construction_root_element
2117    
# Line 2055  sub _reset_insertion_mode ($) { Line 2146  sub _reset_insertion_mode ($) {
2146            
2147        ## Step 4..13        ## Step 4..13
2148        my $new_mode = {        my $new_mode = {
2149                        select => 'in select',                        select => IN_SELECT_IM,
2150                        td => 'in cell',                        td => IN_CELL_IM,
2151                        th => 'in cell',                        th => IN_CELL_IM,
2152                        tr => 'in row',                        tr => IN_ROW_IM,
2153                        tbody => 'in table body',                        tbody => IN_TABLE_BODY_IM,
2154                        thead => 'in table head',                        thead => IN_TABLE_BODY_IM,
2155                        tfoot => 'in table foot',                        tfoot => IN_TABLE_BODY_IM,
2156                        caption => 'in caption',                        caption => IN_CAPTION_IM,
2157                        colgroup => 'in column group',                        colgroup => IN_COLUMN_GROUP_IM,
2158                        table => 'in table',                        table => IN_TABLE_IM,
2159                        head => 'in body', # not in head!                        head => IN_BODY_IM, # not in head!
2160                        body => 'in body',                        body => IN_BODY_IM,
2161                        frameset => 'in frameset',                        frameset => IN_FRAMESET_IM,
2162                       }->{$node->[1]};                       }->{$node->[1]};
2163        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
2164                
2165        ## Step 14        ## Step 14
2166        if ($node->[1] eq 'html') {        if ($node->[1] eq 'html') {
2167          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
2168            $self->{insertion_mode} = 'before head';            $self->{insertion_mode} = BEFORE_HEAD_IM;
2169          } else {          } else {
2170            $self->{insertion_mode} = 'after head';            $self->{insertion_mode} = AFTER_HEAD_IM;
2171          }          }
2172          return;          return;
2173        }        }
2174                
2175        ## Step 15        ## Step 15
2176        $self->{insertion_mode} = 'in body' and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
2177                
2178        ## Step 16        ## Step 16
2179        $i--;        $i--;
# Line 2096  sub _reset_insertion_mode ($) { Line 2187  sub _reset_insertion_mode ($) {
2187  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
2188    my $self = shift;    my $self = shift;
2189    
   my $phase = 'main';  
   
2190    my $active_formatting_elements = [];    my $active_formatting_elements = [];
2191    
2192    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2192  sub _tree_construction_main ($) { Line 2281  sub _tree_construction_main ($) {
2281      $insert->($el); # /context node/->append_child ($el)      $insert->($el); # /context node/->append_child ($el)
2282    
2283      ## Step 3      ## Step 3
2284      $self->{content_model_flag} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
2285      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
2286    
2287      ## Step 4      ## Step 4
2288      my $text = '';      my $text = '';
2289      !!!next-token;      !!!next-token;
2290      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
2291        $text .= $token->{data};        $text .= $token->{data};
2292        !!!next-token;        !!!next-token;
2293      }      }
# Line 2210  sub _tree_construction_main ($) { Line 2299  sub _tree_construction_main ($) {
2299      }      }
2300    
2301      ## Step 6      ## Step 6
2302      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
2303    
2304      ## Step 7      ## Step 7
2305      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {
2306        ## Ignore the token        ## Ignore the token
2307        } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {
2308          !!!parse-error (type => 'in CDATA:#'.$token->{type});
2309        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
2310          !!!parse-error (type => 'in RCDATA:#'.$token->{type});
2311      } else {      } else {
2312        !!!parse-error (type => 'in '.$content_model_flag.':#'.$token->{type});        die "$0: $content_model_flag in parse_rcdata";
2313      }      }
2314      !!!next-token;      !!!next-token;
2315    }; # $parse_rcdata    }; # $parse_rcdata
# Line 2227  sub _tree_construction_main ($) { Line 2320  sub _tree_construction_main ($) {
2320      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, 'script', $token->{attributes});
2321      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
2322    
2323      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
2324      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
2325            
2326      my $text = '';      my $text = '';
2327      !!!next-token;      !!!next-token;
2328      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
2329        $text .= $token->{data};        $text .= $token->{data};
2330        !!!next-token;        !!!next-token;
2331      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
# Line 2240  sub _tree_construction_main ($) { Line 2333  sub _tree_construction_main ($) {
2333        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
2334      }      }
2335                                
2336      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
2337    
2338      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
2339          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
2340        ## Ignore the token        ## Ignore the token
2341      } else {      } else {
# Line 2485  sub _tree_construction_main ($) { Line 2578  sub _tree_construction_main ($) {
2578                         }                         }
2579    }; # $insert_to_foster    }; # $insert_to_foster
2580    
2581    my $in_body = sub {    my $insert;
2582      my $insert = shift;  
2583      if ($token->{type} eq 'start tag') {    B: {
2584        if ($token->{type} == DOCTYPE_TOKEN) {
2585          !!!parse-error (type => 'DOCTYPE in the middle');
2586          ## Ignore the token
2587          ## Stay in the phase
2588          !!!next-token;
2589          redo B;
2590        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2591          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
2592            #
2593          } else {
2594            ## Generate implied end tags
2595            if ({
2596                 dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,
2597                 tbody => 1, tfoot=> 1, thead => 1,
2598                }->{$self->{open_elements}->[-1]->[1]}) {
2599              !!!back-token;
2600              $token = {type => END_TAG_TOKEN, tag_name => $self->{open_elements}->[-1]->[1]};
2601              redo B;
2602            }
2603            
2604            if (@{$self->{open_elements}} > 2 or
2605                (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {
2606              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2607            } elsif (defined $self->{inner_html_node} and
2608                     @{$self->{open_elements}} > 1 and
2609                     $self->{open_elements}->[1]->[1] ne 'body') {
2610              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2611            }
2612    
2613            ## ISSUE: There is an issue in the spec.
2614          }
2615    
2616          ## Stop parsing
2617          last B;
2618        } elsif ($token->{type} == START_TAG_TOKEN and
2619                 $token->{tag_name} eq 'html') {
2620          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
2621            ## Turn into the main phase
2622            !!!parse-error (type => 'after html:html');
2623            $self->{insertion_mode} = AFTER_BODY_IM;
2624          } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
2625            ## Turn into the main phase
2626            !!!parse-error (type => 'after html:html');
2627            $self->{insertion_mode} = AFTER_FRAMESET_IM;
2628          }
2629    
2630    ## ISSUE: "aa<html>" is not a parse error.
2631    ## ISSUE: "<html>" in fragment is not a parse error.
2632          unless ($token->{first_start_tag}) {
2633            !!!parse-error (type => 'not first start tag');
2634          }
2635          my $top_el = $self->{open_elements}->[0]->[0];
2636          for my $attr_name (keys %{$token->{attributes}}) {
2637            unless ($top_el->has_attribute_ns (undef, $attr_name)) {
2638              $top_el->set_attribute_ns
2639                (undef, [undef, $attr_name],
2640                 $token->{attributes}->{$attr_name}->{value});
2641            }
2642          }
2643          !!!next-token;
2644          redo B;
2645        } elsif ($token->{type} == COMMENT_TOKEN) {
2646          my $comment = $self->{document}->create_comment ($token->{data});
2647          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
2648            $self->{document}->append_child ($comment);
2649          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
2650            $self->{open_elements}->[0]->[0]->append_child ($comment);
2651          } else {
2652            $self->{open_elements}->[-1]->[0]->append_child ($comment);
2653          }
2654          !!!next-token;
2655          redo B;
2656        } elsif ($self->{insertion_mode} & HEAD_IMS) {
2657          if ($token->{type} == CHARACTER_TOKEN) {
2658            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
2659              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2660              unless (length $token->{data}) {
2661                !!!next-token;
2662                redo B;
2663              }
2664            }
2665    
2666            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2667              ## As if <head>
2668              !!!create-element ($self->{head_element}, 'head');
2669              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2670              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2671    
2672              ## Reprocess in the "in head" insertion mode...
2673              pop @{$self->{open_elements}};
2674    
2675              ## Reprocess in the "after head" insertion mode...
2676            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2677              ## As if </noscript>
2678              pop @{$self->{open_elements}};
2679              !!!parse-error (type => 'in noscript:#character');
2680              
2681              ## Reprocess in the "in head" insertion mode...
2682              ## As if </head>
2683              pop @{$self->{open_elements}};
2684    
2685              ## Reprocess in the "after head" insertion mode...
2686            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2687              pop @{$self->{open_elements}};
2688    
2689              ## Reprocess in the "after head" insertion mode...
2690            }
2691    
2692                ## "after head" insertion mode
2693                ## As if <body>
2694                !!!insert-element ('body');
2695                $self->{insertion_mode} = IN_BODY_IM;
2696                ## reprocess
2697                redo B;
2698              } elsif ($token->{type} == START_TAG_TOKEN) {
2699                if ($token->{tag_name} eq 'head') {
2700                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2701                    !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});
2702                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2703                    push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];
2704                    $self->{insertion_mode} = IN_HEAD_IM;
2705                    !!!next-token;
2706                    redo B;
2707                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2708                    #
2709                  } else {
2710                    !!!parse-error (type => 'in head:head'); # or in head noscript
2711                    ## Ignore the token
2712                    !!!next-token;
2713                    redo B;
2714                  }
2715                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2716                  ## As if <head>
2717                  !!!create-element ($self->{head_element}, 'head');
2718                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2719                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2720    
2721                  $self->{insertion_mode} = IN_HEAD_IM;
2722                  ## Reprocess in the "in head" insertion mode...
2723                }
2724    
2725                if ($token->{tag_name} eq 'base') {
2726                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2727                    ## As if </noscript>
2728                    pop @{$self->{open_elements}};
2729                    !!!parse-error (type => 'in noscript:base');
2730                  
2731                    $self->{insertion_mode} = IN_HEAD_IM;
2732                    ## Reprocess in the "in head" insertion mode...
2733                  }
2734    
2735                  ## NOTE: There is a "as if in head" code clone.
2736                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2737                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2738                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2739                  }
2740                  !!!insert-element ($token->{tag_name}, $token->{attributes});
2741                  pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
2742                  pop @{$self->{open_elements}}
2743                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2744                  !!!next-token;
2745                  redo B;
2746                } elsif ($token->{tag_name} eq 'link') {
2747                  ## NOTE: There is a "as if in head" code clone.
2748                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2749                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2750                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2751                  }
2752                  !!!insert-element ($token->{tag_name}, $token->{attributes});
2753                  pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
2754                  pop @{$self->{open_elements}}
2755                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2756                  !!!next-token;
2757                  redo B;
2758                } elsif ($token->{tag_name} eq 'meta') {
2759                  ## NOTE: There is a "as if in head" code clone.
2760                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2761                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2762                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2763                  }
2764                  !!!insert-element ($token->{tag_name}, $token->{attributes});
2765                  pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
2766    
2767                  unless ($self->{confident}) {
2768                    my $charset;
2769                    if ($token->{attributes}->{charset}) { ## TODO: And if supported
2770                      $charset = $token->{attributes}->{charset}->{value};
2771                    }
2772                    if ($token->{attributes}->{'http-equiv'}) {
2773                      ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
2774                      if ($token->{attributes}->{'http-equiv'}->{value}
2775                          =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=
2776                              [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
2777                              ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
2778                        $charset = defined $1 ? $1 : defined $2 ? $2 : $3;
2779                      } ## TODO: And if supported
2780                    }
2781                    ## TODO: Change the encoding
2782                  }
2783    
2784                  ## TODO: Extracting |charset| from |meta|.
2785                  pop @{$self->{open_elements}}
2786                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2787                  !!!next-token;
2788                  redo B;
2789                } elsif ($token->{tag_name} eq 'title') {
2790                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2791                    ## As if </noscript>
2792                    pop @{$self->{open_elements}};
2793                    !!!parse-error (type => 'in noscript:title');
2794                  
2795                    $self->{insertion_mode} = IN_HEAD_IM;
2796                    ## Reprocess in the "in head" insertion mode...
2797                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2798                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2799                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2800                  }
2801    
2802                  ## NOTE: There is a "as if in head" code clone.
2803                  my $parent = defined $self->{head_element} ? $self->{head_element}
2804                      : $self->{open_elements}->[-1]->[0];
2805                  $parse_rcdata->(RCDATA_CONTENT_MODEL,
2806                                  sub { $parent->append_child ($_[0]) });
2807                  pop @{$self->{open_elements}}
2808                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2809                  redo B;
2810                } elsif ($token->{tag_name} eq 'style') {
2811                  ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2812                  ## insertion mode IN_HEAD_IM)
2813                  ## NOTE: There is a "as if in head" code clone.
2814                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2815                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2816                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2817                  }
2818                  $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);
2819                  pop @{$self->{open_elements}}
2820                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2821                  redo B;
2822                } elsif ($token->{tag_name} eq 'noscript') {
2823                  if ($self->{insertion_mode} == IN_HEAD_IM) {
2824                    ## NOTE: and scripting is disalbed
2825                    !!!insert-element ($token->{tag_name}, $token->{attributes});
2826                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
2827                    !!!next-token;
2828                    redo B;
2829                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2830                    !!!parse-error (type => 'in noscript:noscript');
2831                    ## Ignore the token
2832                    !!!next-token;
2833                    redo B;
2834                  } else {
2835                    #
2836                  }
2837                } elsif ($token->{tag_name} eq 'script') {
2838                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2839                    ## As if </noscript>
2840                    pop @{$self->{open_elements}};
2841                    !!!parse-error (type => 'in noscript:script');
2842                  
2843                    $self->{insertion_mode} = IN_HEAD_IM;
2844                    ## Reprocess in the "in head" insertion mode...
2845                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2846                    !!!parse-error (type => 'after head:'.$token->{tag_name});
2847                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2848                  }
2849    
2850                  ## NOTE: There is a "as if in head" code clone.
2851                  $script_start_tag->($insert_to_current);
2852                  pop @{$self->{open_elements}}
2853                      if $self->{insertion_mode} == AFTER_HEAD_IM;
2854                  redo B;
2855                } elsif ($token->{tag_name} eq 'body' or
2856                         $token->{tag_name} eq 'frameset') {
2857                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2858                    ## As if </noscript>
2859                    pop @{$self->{open_elements}};
2860                    !!!parse-error (type => 'in noscript:'.$token->{tag_name});
2861                    
2862                    ## Reprocess in the "in head" insertion mode...
2863                    ## As if </head>
2864                    pop @{$self->{open_elements}};
2865                    
2866                    ## Reprocess in the "after head" insertion mode...
2867                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2868                    pop @{$self->{open_elements}};
2869                    
2870                    ## Reprocess in the "after head" insertion mode...
2871                  }
2872    
2873                  ## "after head" insertion mode
2874                  !!!insert-element ($token->{tag_name}, $token->{attributes});
2875                  if ($token->{tag_name} eq 'body') {
2876                    $self->{insertion_mode} = IN_BODY_IM;
2877                  } elsif ($token->{tag_name} eq 'frameset') {
2878                    $self->{insertion_mode} = IN_FRAMESET_IM;
2879                  } else {
2880                    die "$0: tag name: $self->{tag_name}";
2881                  }
2882                  !!!next-token;
2883                  redo B;
2884                } else {
2885                  #
2886                }
2887    
2888                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2889                  ## As if </noscript>
2890                  pop @{$self->{open_elements}};
2891                  !!!parse-error (type => 'in noscript:/'.$token->{tag_name});
2892                  
2893                  ## Reprocess in the "in head" insertion mode...
2894                  ## As if </head>
2895                  pop @{$self->{open_elements}};
2896    
2897                  ## Reprocess in the "after head" insertion mode...
2898                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2899                  ## As if </head>
2900                  pop @{$self->{open_elements}};
2901    
2902                  ## Reprocess in the "after head" insertion mode...
2903                }
2904    
2905                ## "after head" insertion mode
2906                ## As if <body>
2907                !!!insert-element ('body');
2908                $self->{insertion_mode} = IN_BODY_IM;
2909                ## reprocess
2910                redo B;
2911              } elsif ($token->{type} == END_TAG_TOKEN) {
2912                if ($token->{tag_name} eq 'head') {
2913                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2914                    ## As if <head>
2915                    !!!create-element ($self->{head_element}, 'head');
2916                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2917                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2918    
2919                    ## Reprocess in the "in head" insertion mode...
2920                    pop @{$self->{open_elements}};
2921                    $self->{insertion_mode} = AFTER_HEAD_IM;
2922                    !!!next-token;
2923                    redo B;
2924                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2925                    ## As if </noscript>
2926                    pop @{$self->{open_elements}};
2927                    !!!parse-error (type => 'in noscript:script');
2928                    
2929                    ## Reprocess in the "in head" insertion mode...
2930                    pop @{$self->{open_elements}};
2931                    $self->{insertion_mode} = AFTER_HEAD_IM;
2932                    !!!next-token;
2933                    redo B;
2934                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2935                    pop @{$self->{open_elements}};
2936                    $self->{insertion_mode} = AFTER_HEAD_IM;
2937                    !!!next-token;
2938                    redo B;
2939                  } else {
2940                    #
2941                  }
2942                } elsif ($token->{tag_name} eq 'noscript') {
2943                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2944                    pop @{$self->{open_elements}};
2945                    $self->{insertion_mode} = IN_HEAD_IM;
2946                    !!!next-token;
2947                    redo B;
2948                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2949                    !!!parse-error (type => 'unmatched end tag:noscript');
2950                    ## Ignore the token ## ISSUE: An issue in the spec.
2951                    !!!next-token;
2952                    redo B;
2953                  } else {
2954                    #
2955                  }
2956                } elsif ({
2957                          body => 1, html => 1,
2958                         }->{$token->{tag_name}}) {
2959                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2960                    ## As if <head>
2961                    !!!create-element ($self->{head_element}, 'head');
2962                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2963                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2964    
2965                    $self->{insertion_mode} = IN_HEAD_IM;
2966                    ## Reprocess in the "in head" insertion mode...
2967                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2968                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2969                    ## Ignore the token
2970                    !!!next-token;
2971                    redo B;
2972                  }
2973                  
2974                  #
2975                } elsif ({
2976                          p => 1, br => 1,
2977                         }->{$token->{tag_name}}) {
2978                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2979                    ## As if <head>
2980                    !!!create-element ($self->{head_element}, 'head');
2981                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2982                    push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2983    
2984                    $self->{insertion_mode} = IN_HEAD_IM;
2985                    ## Reprocess in the "in head" insertion mode...
2986                  }
2987    
2988                  #
2989                } else {
2990                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2991                    #
2992                  } else {
2993                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2994                    ## Ignore the token
2995                    !!!next-token;
2996                    redo B;
2997                  }
2998                }
2999    
3000                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3001                  ## As if </noscript>
3002                  pop @{$self->{open_elements}};
3003                  !!!parse-error (type => 'in noscript:/'.$token->{tag_name});
3004                  
3005                  ## Reprocess in the "in head" insertion mode...
3006                  ## As if </head>
3007                  pop @{$self->{open_elements}};
3008    
3009                  ## Reprocess in the "after head" insertion mode...
3010                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3011                  ## As if </head>
3012                  pop @{$self->{open_elements}};
3013    
3014                  ## Reprocess in the "after head" insertion mode...
3015                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3016                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3017                  ## Ignore the token ## ISSUE: An issue in the spec.
3018                  !!!next-token;
3019                  redo B;
3020                }
3021    
3022                ## "after head" insertion mode
3023                ## As if <body>
3024                !!!insert-element ('body');
3025                $self->{insertion_mode} = IN_BODY_IM;
3026                ## reprocess
3027                redo B;
3028              } else {
3029                die "$0: $token->{type}: Unknown token type";
3030              }
3031    
3032              ## ISSUE: An issue in the spec.
3033        } elsif ($self->{insertion_mode} & BODY_IMS) {
3034              if ($token->{type} == CHARACTER_TOKEN) {
3035                ## NOTE: There is a code clone of "character in body".
3036                $reconstruct_active_formatting_elements->($insert_to_current);
3037                
3038                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3039    
3040                !!!next-token;
3041                redo B;
3042              } elsif ($token->{type} == START_TAG_TOKEN) {
3043                if ({
3044                     caption => 1, col => 1, colgroup => 1, tbody => 1,
3045                     td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
3046                    }->{$token->{tag_name}}) {
3047                  if ($self->{insertion_mode} == IN_CELL_IM) {
3048                    ## have an element in table scope
3049                    my $tn;
3050                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3051                      my $node = $self->{open_elements}->[$_];
3052                      if ($node->[1] eq 'td' or $node->[1] eq 'th') {
3053                        $tn = $node->[1];
3054                        last INSCOPE;
3055                      } elsif ({
3056                                table => 1, html => 1,
3057                               }->{$node->[1]}) {
3058                        last INSCOPE;
3059                      }
3060                    } # INSCOPE
3061                      unless (defined $tn) {
3062                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3063                        ## Ignore the token
3064                        !!!next-token;
3065                        redo B;
3066                      }
3067                    
3068                    ## Close the cell
3069                    !!!back-token; # <?>
3070                    $token = {type => END_TAG_TOKEN, tag_name => $tn};
3071                    redo B;
3072                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
3073                    !!!parse-error (type => 'not closed:caption');
3074                    
3075                    ## As if </caption>
3076                    ## have a table element in table scope
3077                    my $i;
3078                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3079                      my $node = $self->{open_elements}->[$_];
3080                      if ($node->[1] eq 'caption') {
3081                        $i = $_;
3082                        last INSCOPE;
3083                      } elsif ({
3084                                table => 1, html => 1,
3085                               }->{$node->[1]}) {
3086                        last INSCOPE;
3087                      }
3088                    } # INSCOPE
3089                      unless (defined $i) {
3090                        !!!parse-error (type => 'unmatched end tag:caption');
3091                        ## Ignore the token
3092                        !!!next-token;
3093                        redo B;
3094                      }
3095                    
3096                    ## generate implied end tags
3097                    if ({
3098                         dd => 1, dt => 1, li => 1, p => 1,
3099                         td => 1, th => 1, tr => 1,
3100                         tbody => 1, tfoot=> 1, thead => 1,
3101                        }->{$self->{open_elements}->[-1]->[1]}) {
3102                      !!!back-token; # <?>
3103                      $token = {type => END_TAG_TOKEN, tag_name => 'caption'};
3104                      !!!back-token;
3105                      $token = {type => END_TAG_TOKEN,
3106                                tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3107                      redo B;
3108                    }
3109    
3110                    if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3111                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3112                    }
3113                    
3114                    splice @{$self->{open_elements}}, $i;
3115                    
3116                    $clear_up_to_marker->();
3117                    
3118                    $self->{insertion_mode} = IN_TABLE_IM;
3119                    
3120                    ## reprocess
3121                    redo B;
3122                  } else {
3123                    #
3124                  }
3125                } else {
3126                  #
3127                }
3128              } elsif ($token->{type} == END_TAG_TOKEN) {
3129                if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
3130                  if ($self->{insertion_mode} == IN_CELL_IM) {
3131                    ## have an element in table scope
3132                    my $i;
3133                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3134                      my $node = $self->{open_elements}->[$_];
3135                      if ($node->[1] eq $token->{tag_name}) {
3136                        $i = $_;
3137                        last INSCOPE;
3138                      } elsif ({
3139                                table => 1, html => 1,
3140                               }->{$node->[1]}) {
3141                        last INSCOPE;
3142                      }
3143                    } # INSCOPE
3144                      unless (defined $i) {
3145                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3146                        ## Ignore the token
3147                        !!!next-token;
3148                        redo B;
3149                      }
3150                    
3151                    ## generate implied end tags
3152                    if ({
3153                         dd => 1, dt => 1, li => 1, p => 1,
3154                         td => ($token->{tag_name} eq 'th'),
3155                         th => ($token->{tag_name} eq 'td'),
3156                         tr => 1,
3157                         tbody => 1, tfoot=> 1, thead => 1,
3158                        }->{$self->{open_elements}->[-1]->[1]}) {
3159                      !!!back-token;
3160                      $token = {type => END_TAG_TOKEN,
3161                                tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3162                      redo B;
3163                    }
3164                    
3165                    if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
3166                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3167                    }
3168                    
3169                    splice @{$self->{open_elements}}, $i;
3170                    
3171                    $clear_up_to_marker->();
3172                    
3173                    $self->{insertion_mode} = IN_ROW_IM;
3174                    
3175                    !!!next-token;
3176                    redo B;
3177                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
3178                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3179                    ## Ignore the token
3180                    !!!next-token;
3181                    redo B;
3182                  } else {
3183                    #
3184                  }
3185                } elsif ($token->{tag_name} eq 'caption') {
3186                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
3187                    ## have a table element in table scope
3188                    my $i;
3189                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3190                      my $node = $self->{open_elements}->[$_];
3191                      if ($node->[1] eq $token->{tag_name}) {
3192                        $i = $_;
3193                        last INSCOPE;
3194                      } elsif ({
3195                                table => 1, html => 1,
3196                               }->{$node->[1]}) {
3197                        last INSCOPE;
3198                      }
3199                    } # INSCOPE
3200                      unless (defined $i) {
3201                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3202                        ## Ignore the token
3203                        !!!next-token;
3204                        redo B;
3205                      }
3206                    
3207                    ## generate implied end tags
3208                    if ({
3209                         dd => 1, dt => 1, li => 1, p => 1,
3210                         td => 1, th => 1, tr => 1,
3211                         tbody => 1, tfoot=> 1, thead => 1,
3212                        }->{$self->{open_elements}->[-1]->[1]}) {
3213                      !!!back-token;
3214                      $token = {type => END_TAG_TOKEN,
3215                                tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3216                      redo B;
3217                    }
3218                    
3219                    if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3220                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3221                    }
3222                    
3223                    splice @{$self->{open_elements}}, $i;
3224                    
3225                    $clear_up_to_marker->();
3226                    
3227                    $self->{insertion_mode} = IN_TABLE_IM;
3228                    
3229                    !!!next-token;
3230                    redo B;
3231                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
3232                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3233                    ## Ignore the token
3234                    !!!next-token;
3235                    redo B;
3236                  } else {
3237                    #
3238                  }
3239                } elsif ({
3240                          table => 1, tbody => 1, tfoot => 1,
3241                          thead => 1, tr => 1,
3242                         }->{$token->{tag_name}} and
3243                         $self->{insertion_mode} == IN_CELL_IM) {
3244                  ## have an element in table scope
3245                  my $i;
3246                  my $tn;
3247                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3248                    my $node = $self->{open_elements}->[$_];
3249                    if ($node->[1] eq $token->{tag_name}) {
3250                      $i = $_;
3251                      last INSCOPE;
3252                    } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {
3253                      $tn = $node->[1];
3254                      ## NOTE: There is exactly one |td| or |th| element
3255                      ## in scope in the stack of open elements by definition.
3256                    } elsif ({
3257                              table => 1, html => 1,
3258                             }->{$node->[1]}) {
3259                      last INSCOPE;
3260                    }
3261                  } # INSCOPE
3262                  unless (defined $i) {
3263                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3264                    ## Ignore the token
3265                    !!!next-token;
3266                    redo B;
3267                  }
3268    
3269                  ## Close the cell
3270                  !!!back-token; # </?>
3271                  $token = {type => END_TAG_TOKEN, tag_name => $tn};
3272                  redo B;
3273                } elsif ($token->{tag_name} eq 'table' and
3274                         $self->{insertion_mode} == IN_CAPTION_IM) {
3275                  !!!parse-error (type => 'not closed:caption');
3276    
3277                  ## As if </caption>
3278                  ## have a table element in table scope
3279                  my $i;
3280                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3281                    my $node = $self->{open_elements}->[$_];
3282                    if ($node->[1] eq 'caption') {
3283                      $i = $_;
3284                      last INSCOPE;
3285                    } elsif ({
3286                              table => 1, html => 1,
3287                             }->{$node->[1]}) {
3288                      last INSCOPE;
3289                    }
3290                  } # INSCOPE
3291                  unless (defined $i) {
3292                    !!!parse-error (type => 'unmatched end tag:caption');
3293                    ## Ignore the token
3294                    !!!next-token;
3295                    redo B;
3296                  }
3297                  
3298                  ## generate implied end tags
3299                  if ({
3300                       dd => 1, dt => 1, li => 1, p => 1,
3301                       td => 1, th => 1, tr => 1,
3302                       tbody => 1, tfoot=> 1, thead => 1,
3303                      }->{$self->{open_elements}->[-1]->[1]}) {
3304                    !!!back-token; # </table>
3305                    $token = {type => END_TAG_TOKEN, tag_name => 'caption'};
3306                    !!!back-token;
3307                    $token = {type => END_TAG_TOKEN,
3308                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3309                    redo B;
3310                  }
3311    
3312                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3313                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3314                  }
3315    
3316                  splice @{$self->{open_elements}}, $i;
3317    
3318                  $clear_up_to_marker->();
3319    
3320                  $self->{insertion_mode} = IN_TABLE_IM;
3321    
3322                  ## reprocess
3323                  redo B;
3324                } elsif ({
3325                          body => 1, col => 1, colgroup => 1, html => 1,
3326                         }->{$token->{tag_name}}) {
3327                  if ($self->{insertion_mode} & BODY_TABLE_IMS) {
3328                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3329                    ## Ignore the token
3330                    !!!next-token;
3331                    redo B;
3332                  } else {
3333                    #
3334                  }
3335                } elsif ({
3336                          tbody => 1, tfoot => 1,
3337                          thead => 1, tr => 1,
3338                         }->{$token->{tag_name}} and
3339                         $self->{insertion_mode} == IN_CAPTION_IM) {
3340                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3341                  ## Ignore the token
3342                  !!!next-token;
3343                  redo B;
3344                } else {
3345                  #
3346                }
3347          } else {
3348            die "$0: $token->{type}: Unknown token type";
3349          }
3350    
3351          $insert = $insert_to_current;
3352          #
3353        } elsif ($self->{insertion_mode} & TABLE_IMS) {
3354          if ($token->{type} == CHARACTER_TOKEN) {
3355                if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3356                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3357                  
3358                  unless (length $token->{data}) {
3359                    !!!next-token;
3360                    redo B;
3361                  }
3362                }
3363    
3364                !!!parse-error (type => 'in table:#character');
3365    
3366                ## As if in body, but insert into foster parent element
3367                ## ISSUE: Spec says that "whenever a node would be inserted
3368                ## into the current node" while characters might not be
3369                ## result in a new Text node.
3370                $reconstruct_active_formatting_elements->($insert_to_foster);
3371                
3372                if ({
3373                     table => 1, tbody => 1, tfoot => 1,
3374                     thead => 1, tr => 1,
3375                    }->{$self->{open_elements}->[-1]->[1]}) {
3376                  # MUST
3377                  my $foster_parent_element;
3378                  my $next_sibling;
3379                  my $prev_sibling;
3380                  OE: for (reverse 0..$#{$self->{open_elements}}) {
3381                    if ($self->{open_elements}->[$_]->[1] eq 'table') {
3382                      my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3383                      if (defined $parent and $parent->node_type == 1) {
3384                        $foster_parent_element = $parent;
3385                        $next_sibling = $self->{open_elements}->[$_]->[0];
3386                        $prev_sibling = $next_sibling->previous_sibling;
3387                      } else {
3388                        $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3389                        $prev_sibling = $foster_parent_element->last_child;
3390                      }
3391                      last OE;
3392                    }
3393                  } # OE
3394                  $foster_parent_element = $self->{open_elements}->[0]->[0] and
3395                  $prev_sibling = $foster_parent_element->last_child
3396                    unless defined $foster_parent_element;
3397                  if (defined $prev_sibling and
3398                      $prev_sibling->node_type == 3) {
3399                    $prev_sibling->manakai_append_text ($token->{data});
3400                  } else {
3401                    $foster_parent_element->insert_before
3402                      ($self->{document}->create_text_node ($token->{data}),
3403                       $next_sibling);
3404                  }
3405                } else {
3406                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3407                }
3408                
3409                !!!next-token;
3410                redo B;
3411          } elsif ($token->{type} == START_TAG_TOKEN) {
3412                if ({
3413                     tr => ($self->{insertion_mode} != IN_ROW_IM),
3414                     th => 1, td => 1,
3415                    }->{$token->{tag_name}}) {
3416                  if ($self->{insertion_mode} == IN_TABLE_IM) {
3417                    ## Clear back to table context
3418                    while ($self->{open_elements}->[-1]->[1] ne 'table' and
3419                           $self->{open_elements}->[-1]->[1] ne 'html') {
3420                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3421                      pop @{$self->{open_elements}};
3422                    }
3423                    
3424                    !!!insert-element ('tbody');
3425                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
3426                    ## reprocess in the "in table body" insertion mode...
3427                  }
3428    
3429                  if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3430                    unless ($token->{tag_name} eq 'tr') {
3431                      !!!parse-error (type => 'missing start tag:tr');
3432                    }
3433                    
3434                    ## Clear back to table body context
3435                    while (not {
3436                      tbody => 1, tfoot => 1, thead => 1, html => 1,
3437                    }->{$self->{open_elements}->[-1]->[1]}) {
3438                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3439                      pop @{$self->{open_elements}};
3440                    }
3441                    
3442                    $self->{insertion_mode} = IN_ROW_IM;
3443                    if ($token->{tag_name} eq 'tr') {
3444                      !!!insert-element ($token->{tag_name}, $token->{attributes});
3445                      !!!next-token;
3446                      redo B;
3447                    } else {
3448                      !!!insert-element ('tr');
3449                      ## reprocess in the "in row" insertion mode
3450                    }
3451                  }
3452    
3453                  ## Clear back to table row context
3454                  while (not {
3455                    tr => 1, html => 1,
3456                  }->{$self->{open_elements}->[-1]->[1]}) {
3457                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3458                    pop @{$self->{open_elements}};
3459                  }
3460                  
3461                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3462                  $self->{insertion_mode} = IN_CELL_IM;
3463    
3464                  push @$active_formatting_elements, ['#marker', ''];
3465                  
3466                  !!!next-token;
3467                  redo B;
3468                } elsif ({
3469                          caption => 1, col => 1, colgroup => 1,
3470                          tbody => 1, tfoot => 1, thead => 1,
3471                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3472                         }->{$token->{tag_name}}) {
3473                  if ($self->{insertion_mode} == IN_ROW_IM) {
3474                    ## As if </tr>
3475                    ## have an element in table scope
3476                    my $i;
3477                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3478                      my $node = $self->{open_elements}->[$_];
3479                      if ($node->[1] eq 'tr') {
3480                        $i = $_;
3481                        last INSCOPE;
3482                      } elsif ({
3483                                table => 1, html => 1,
3484                               }->{$node->[1]}) {
3485                        last INSCOPE;
3486                      }
3487                    } # INSCOPE
3488                    unless (defined $i) {
3489                      !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});
3490                      ## Ignore the token
3491                      !!!next-token;
3492                      redo B;
3493                    }
3494                    
3495                    ## Clear back to table row context
3496                    while (not {
3497                      tr => 1, html => 1,
3498                    }->{$self->{open_elements}->[-1]->[1]}) {
3499                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3500                      pop @{$self->{open_elements}};
3501                    }
3502                    
3503                    pop @{$self->{open_elements}}; # tr
3504                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
3505                    if ($token->{tag_name} eq 'tr') {
3506                      ## reprocess
3507                      redo B;
3508                    } else {
3509                      ## reprocess in the "in table body" insertion mode...
3510                    }
3511                  }
3512    
3513                  if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3514                    ## have an element in table scope
3515                    my $i;
3516                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3517                      my $node = $self->{open_elements}->[$_];
3518                      if ({
3519                           tbody => 1, thead => 1, tfoot => 1,
3520                          }->{$node->[1]}) {
3521                        $i = $_;
3522                        last INSCOPE;
3523                      } elsif ({
3524                                table => 1, html => 1,
3525                               }->{$node->[1]}) {
3526                        last INSCOPE;
3527                      }
3528                    } # INSCOPE
3529                    unless (defined $i) {
3530                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3531                      ## Ignore the token
3532                      !!!next-token;
3533                      redo B;
3534                    }
3535    
3536                    ## Clear back to table body context
3537                    while (not {
3538                      tbody => 1, tfoot => 1, thead => 1, html => 1,
3539                    }->{$self->{open_elements}->[-1]->[1]}) {
3540                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3541                      pop @{$self->{open_elements}};
3542                    }
3543                    
3544                    ## As if <{current node}>
3545                    ## have an element in table scope
3546                    ## true by definition
3547                    
3548                    ## Clear back to table body context
3549                    ## nop by definition
3550                    
3551                    pop @{$self->{open_elements}};
3552                    $self->{insertion_mode} = IN_TABLE_IM;
3553                    ## reprocess in "in table" insertion mode...
3554                  }
3555    
3556                  if ($token->{tag_name} eq 'col') {
3557                    ## Clear back to table context
3558                    while ($self->{open_elements}->[-1]->[1] ne 'table' and
3559                           $self->{open_elements}->[-1]->[1] ne 'html') {
3560                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3561                      pop @{$self->{open_elements}};
3562                    }
3563                    
3564                    !!!insert-element ('colgroup');
3565                    $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3566                    ## reprocess
3567                    redo B;
3568                  } elsif ({
3569                            caption => 1,
3570                            colgroup => 1,
3571                            tbody => 1, tfoot => 1, thead => 1,
3572                           }->{$token->{tag_name}}) {
3573                    ## Clear back to table context
3574                    while ($self->{open_elements}->[-1]->[1] ne 'table' and
3575                           $self->{open_elements}->[-1]->[1] ne 'html') {
3576                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3577                      pop @{$self->{open_elements}};
3578                    }
3579                    
3580                    push @$active_formatting_elements, ['#marker', '']
3581                        if $token->{tag_name} eq 'caption';
3582                    
3583                    !!!insert-element ($token->{tag_name}, $token->{attributes});
3584                    $self->{insertion_mode} = {
3585                                               caption => IN_CAPTION_IM,
3586                                               colgroup => IN_COLUMN_GROUP_IM,
3587                                               tbody => IN_TABLE_BODY_IM,
3588                                               tfoot => IN_TABLE_BODY_IM,
3589                                               thead => IN_TABLE_BODY_IM,
3590                                              }->{$token->{tag_name}};
3591                    !!!next-token;
3592                    redo B;
3593                  } else {
3594                    die "$0: in table: <>: $token->{tag_name}";
3595                  }
3596                } elsif ($token->{tag_name} eq 'table') {
3597                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3598    
3599                  ## As if </table>
3600                  ## have a table element in table scope
3601                  my $i;
3602                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3603                    my $node = $self->{open_elements}->[$_];
3604                    if ($node->[1] eq 'table') {
3605                      $i = $_;
3606                      last INSCOPE;
3607                    } elsif ({
3608                              table => 1, html => 1,
3609                             }->{$node->[1]}) {
3610                      last INSCOPE;
3611                    }
3612                  } # INSCOPE
3613                  unless (defined $i) {
3614                    !!!parse-error (type => 'unmatched end tag:table');
3615                    ## Ignore tokens </table><table>
3616                    !!!next-token;
3617                    redo B;
3618                  }
3619                  
3620                  ## generate implied end tags
3621                  if ({
3622                       dd => 1, dt => 1, li => 1, p => 1,
3623                       td => 1, th => 1, tr => 1,
3624                       tbody => 1, tfoot=> 1, thead => 1,
3625                      }->{$self->{open_elements}->[-1]->[1]}) {
3626                    !!!back-token; # <table>
3627                    $token = {type => END_TAG_TOKEN, tag_name => 'table'};
3628                    !!!back-token;
3629                    $token = {type => END_TAG_TOKEN,
3630                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3631                    redo B;
3632                  }
3633    
3634                  if ($self->{open_elements}->[-1]->[1] ne 'table') {
3635                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3636                  }
3637    
3638                  splice @{$self->{open_elements}}, $i;
3639    
3640                  $self->_reset_insertion_mode;
3641    
3642                  ## reprocess
3643                  redo B;
3644            } else {
3645              !!!parse-error (type => 'in table:'.$token->{tag_name});
3646    
3647              $insert = $insert_to_foster;
3648              #
3649            }
3650          } elsif ($token->{type} == END_TAG_TOKEN) {
3651                if ($token->{tag_name} eq 'tr' and
3652                    $self->{insertion_mode} == IN_ROW_IM) {
3653                  ## have an element in table scope
3654                  my $i;
3655                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3656                    my $node = $self->{open_elements}->[$_];
3657                    if ($node->[1] eq $token->{tag_name}) {
3658                      $i = $_;
3659                      last INSCOPE;
3660                    } elsif ({
3661                              table => 1, html => 1,
3662                             }->{$node->[1]}) {
3663                      last INSCOPE;
3664                    }
3665                  } # INSCOPE
3666                  unless (defined $i) {
3667                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3668                    ## Ignore the token
3669                    !!!next-token;
3670                    redo B;
3671                  }
3672    
3673                  ## Clear back to table row context
3674                  while (not {
3675                    tr => 1, html => 1,
3676                  }->{$self->{open_elements}->[-1]->[1]}) {
3677                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3678                    pop @{$self->{open_elements}};
3679                  }
3680    
3681                  pop @{$self->{open_elements}}; # tr
3682                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
3683                  !!!next-token;
3684                  redo B;
3685                } elsif ($token->{tag_name} eq 'table') {
3686                  if ($self->{insertion_mode} == IN_ROW_IM) {
3687                    ## As if </tr>
3688                    ## have an element in table scope
3689                    my $i;
3690                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3691                      my $node = $self->{open_elements}->[$_];
3692                      if ($node->[1] eq 'tr') {
3693                        $i = $_;
3694                        last INSCOPE;
3695                      } elsif ({
3696                                table => 1, html => 1,
3697                               }->{$node->[1]}) {
3698                        last INSCOPE;
3699                      }
3700                    } # INSCOPE
3701                    unless (defined $i) {
3702                      !!!parse-error (type => 'unmatched end tag:'.$token->{type});
3703                      ## Ignore the token
3704                      !!!next-token;
3705                      redo B;
3706                    }
3707                    
3708                    ## Clear back to table row context
3709                    while (not {
3710                      tr => 1, html => 1,
3711                    }->{$self->{open_elements}->[-1]->[1]}) {
3712                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3713                      pop @{$self->{open_elements}};
3714                    }
3715                    
3716                    pop @{$self->{open_elements}}; # tr
3717                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
3718                    ## reprocess in the "in table body" insertion mode...
3719                  }
3720    
3721                  if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3722                    ## have an element in table scope
3723                    my $i;
3724                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3725                      my $node = $self->{open_elements}->[$_];
3726                      if ({
3727                           tbody => 1, thead => 1, tfoot => 1,
3728                          }->{$node->[1]}) {
3729                        $i = $_;
3730                        last INSCOPE;
3731                      } elsif ({
3732                                table => 1, html => 1,
3733                               }->{$node->[1]}) {
3734                        last INSCOPE;
3735                      }
3736                    } # INSCOPE
3737                    unless (defined $i) {
3738                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3739                      ## Ignore the token
3740                      !!!next-token;
3741                      redo B;
3742                    }
3743                    
3744                    ## Clear back to table body context
3745                    while (not {
3746                      tbody => 1, tfoot => 1, thead => 1, html => 1,
3747                    }->{$self->{open_elements}->[-1]->[1]}) {
3748                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3749                      pop @{$self->{open_elements}};
3750                    }
3751                    
3752                    ## As if <{current node}>
3753                    ## have an element in table scope
3754                    ## true by definition
3755                    
3756                    ## Clear back to table body context
3757                    ## nop by definition
3758                    
3759                    pop @{$self->{open_elements}};
3760                    $self->{insertion_mode} = IN_TABLE_IM;
3761                    ## reprocess in the "in table" insertion mode...
3762                  }
3763    
3764                  ## have a table element in table scope
3765                  my $i;
3766                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3767                    my $node = $self->{open_elements}->[$_];
3768                    if ($node->[1] eq $token->{tag_name}) {
3769                      $i = $_;
3770                      last INSCOPE;
3771                    } elsif ({
3772                              table => 1, html => 1,
3773                             }->{$node->[1]}) {
3774                      last INSCOPE;
3775                    }
3776                  } # INSCOPE
3777                  unless (defined $i) {
3778                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3779                    ## Ignore the token
3780                    !!!next-token;
3781                    redo B;
3782                  }
3783    
3784                  ## generate implied end tags
3785                  if ({
3786                       dd => 1, dt => 1, li => 1, p => 1,
3787                       td => 1, th => 1, tr => 1,
3788                       tbody => 1, tfoot=> 1, thead => 1,
3789                      }->{$self->{open_elements}->[-1]->[1]}) {
3790                    !!!back-token;
3791                    $token = {type => END_TAG_TOKEN,
3792                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3793                    redo B;
3794                  }
3795                  
3796                  if ($self->{open_elements}->[-1]->[1] ne 'table') {
3797                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3798                  }
3799                    
3800                  splice @{$self->{open_elements}}, $i;
3801                  
3802                  $self->_reset_insertion_mode;
3803                  
3804                  !!!next-token;
3805                  redo B;
3806                } elsif ({
3807                          tbody => 1, tfoot => 1, thead => 1,
3808                         }->{$token->{tag_name}} and
3809                         $self->{insertion_mode} & ROW_IMS) {
3810                  if ($self->{insertion_mode} == IN_ROW_IM) {
3811                    ## have an element in table scope
3812                    my $i;
3813                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3814                      my $node = $self->{open_elements}->[$_];
3815                      if ($node->[1] eq $token->{tag_name}) {
3816                        $i = $_;
3817                        last INSCOPE;
3818                      } elsif ({
3819                                table => 1, html => 1,
3820                               }->{$node->[1]}) {
3821                        last INSCOPE;
3822                      }
3823                    } # INSCOPE
3824                      unless (defined $i) {
3825                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3826                        ## Ignore the token
3827                        !!!next-token;
3828                        redo B;
3829                      }
3830                    
3831                    ## As if </tr>
3832                    ## have an element in table scope
3833                    my $i;
3834                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3835                      my $node = $self->{open_elements}->[$_];
3836                      if ($node->[1] eq 'tr') {
3837                        $i = $_;
3838                        last INSCOPE;
3839                      } elsif ({
3840                                table => 1, html => 1,
3841                               }->{$node->[1]}) {
3842                        last INSCOPE;
3843                      }
3844                    } # INSCOPE
3845                      unless (defined $i) {
3846                        !!!parse-error (type => 'unmatched end tag:tr');
3847                        ## Ignore the token
3848                        !!!next-token;
3849                        redo B;
3850                      }
3851                    
3852                    ## Clear back to table row context
3853                    while (not {
3854                      tr => 1, html => 1,
3855                    }->{$self->{open_elements}->[-1]->[1]}) {
3856                      !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3857                      pop @{$self->{open_elements}};
3858                    }
3859                    
3860                    pop @{$self->{open_elements}}; # tr
3861                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
3862                    ## reprocess in the "in table body" insertion mode...
3863                  }
3864    
3865                  ## have an element in table scope
3866                  my $i;
3867                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3868                    my $node = $self->{open_elements}->[$_];
3869                    if ($node->[1] eq $token->{tag_name}) {
3870                      $i = $_;
3871                      last INSCOPE;
3872                    } elsif ({
3873                              table => 1, html => 1,
3874                             }->{$node->[1]}) {
3875                      last INSCOPE;
3876                    }
3877                  } # INSCOPE
3878                  unless (defined $i) {
3879                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3880                    ## Ignore the token
3881                    !!!next-token;
3882                    redo B;
3883                  }
3884    
3885                  ## Clear back to table body context
3886                  while (not {
3887                    tbody => 1, tfoot => 1, thead => 1, html => 1,
3888                  }->{$self->{open_elements}->[-1]->[1]}) {
3889                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3890                    pop @{$self->{open_elements}};
3891                  }
3892    
3893                  pop @{$self->{open_elements}};
3894                  $self->{insertion_mode} = IN_TABLE_IM;
3895                  !!!next-token;
3896                  redo B;
3897                } elsif ({
3898                          body => 1, caption => 1, col => 1, colgroup => 1,
3899                          html => 1, td => 1, th => 1,
3900                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3901                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
3902                         }->{$token->{tag_name}}) {
3903                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3904                  ## Ignore the token
3905                  !!!next-token;
3906                  redo B;
3907            } else {
3908              !!!parse-error (type => 'in table:/'.$token->{tag_name});
3909    
3910              $insert = $insert_to_foster;
3911              #
3912            }
3913          } else {
3914            die "$0: $token->{type}: Unknown token type";
3915          }
3916        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
3917              if ($token->{type} == CHARACTER_TOKEN) {
3918                if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3919                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3920                  unless (length $token->{data}) {
3921                    !!!next-token;
3922                    redo B;
3923                  }
3924                }
3925                
3926                #
3927              } elsif ($token->{type} == START_TAG_TOKEN) {
3928                if ($token->{tag_name} eq 'col') {
3929                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3930                  pop @{$self->{open_elements}};
3931                  !!!next-token;
3932                  redo B;
3933                } else {
3934                  #
3935                }
3936              } elsif ($token->{type} == END_TAG_TOKEN) {
3937                if ($token->{tag_name} eq 'colgroup') {
3938                  if ($self->{open_elements}->[-1]->[1] eq 'html') {
3939                    !!!parse-error (type => 'unmatched end tag:colgroup');
3940                    ## Ignore the token
3941                    !!!next-token;
3942                    redo B;
3943                  } else {
3944                    pop @{$self->{open_elements}}; # colgroup
3945                    $self->{insertion_mode} = IN_TABLE_IM;
3946                    !!!next-token;
3947                    redo B;            
3948                  }
3949                } elsif ($token->{tag_name} eq 'col') {
3950                  !!!parse-error (type => 'unmatched end tag:col');
3951                  ## Ignore the token
3952                  !!!next-token;
3953                  redo B;
3954                } else {
3955                  #
3956                }
3957              } else {
3958                #
3959              }
3960    
3961              ## As if </colgroup>
3962              if ($self->{open_elements}->[-1]->[1] eq 'html') {
3963                !!!parse-error (type => 'unmatched end tag:colgroup');
3964                ## Ignore the token
3965                !!!next-token;
3966                redo B;
3967              } else {
3968                pop @{$self->{open_elements}}; # colgroup
3969                $self->{insertion_mode} = IN_TABLE_IM;
3970                ## reprocess
3971                redo B;
3972              }
3973        } elsif ($self->{insertion_mode} == IN_SELECT_IM) {
3974          if ($token->{type} == CHARACTER_TOKEN) {
3975            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3976            !!!next-token;
3977            redo B;
3978          } elsif ($token->{type} == START_TAG_TOKEN) {
3979                if ($token->{tag_name} eq 'option') {
3980                  if ($self->{open_elements}->[-1]->[1] eq 'option') {
3981                    ## As if </option>
3982                    pop @{$self->{open_elements}};
3983                  }
3984    
3985                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3986                  !!!next-token;
3987                  redo B;
3988                } elsif ($token->{tag_name} eq 'optgroup') {
3989                  if ($self->{open_elements}->[-1]->[1] eq 'option') {
3990                    ## As if </option>
3991                    pop @{$self->{open_elements}};
3992                  }
3993    
3994                  if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
3995                    ## As if </optgroup>
3996                    pop @{$self->{open_elements}};
3997                  }
3998    
3999                  !!!insert-element ($token->{tag_name}, $token->{attributes});
4000                  !!!next-token;
4001                  redo B;
4002                } elsif ($token->{tag_name} eq 'select') {
4003                  !!!parse-error (type => 'not closed:select');
4004                  ## As if </select> instead
4005                  ## have an element in table scope
4006                  my $i;
4007                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4008                    my $node = $self->{open_elements}->[$_];
4009                    if ($node->[1] eq $token->{tag_name}) {
4010                      $i = $_;
4011                      last INSCOPE;
4012                    } elsif ({
4013                              table => 1, html => 1,
4014                             }->{$node->[1]}) {
4015                      last INSCOPE;
4016                    }
4017                  } # INSCOPE
4018                  unless (defined $i) {
4019                    !!!parse-error (type => 'unmatched end tag:select');
4020                    ## Ignore the token
4021                    !!!next-token;
4022                    redo B;
4023                  }
4024                  
4025                  splice @{$self->{open_elements}}, $i;
4026    
4027                  $self->_reset_insertion_mode;
4028    
4029                  !!!next-token;
4030                  redo B;
4031            } else {
4032              !!!parse-error (type => 'in select:'.$token->{tag_name});
4033              ## Ignore the token
4034              !!!next-token;
4035              redo B;
4036            }
4037          } elsif ($token->{type} == END_TAG_TOKEN) {
4038                if ($token->{tag_name} eq 'optgroup') {
4039                  if ($self->{open_elements}->[-1]->[1] eq 'option' and
4040                      $self->{open_elements}->[-2]->[1] eq 'optgroup') {
4041                    ## As if </option>
4042                    splice @{$self->{open_elements}}, -2;
4043                  } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
4044                    pop @{$self->{open_elements}};
4045                  } else {
4046                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4047                    ## Ignore the token
4048                  }
4049                  !!!next-token;
4050                  redo B;
4051                } elsif ($token->{tag_name} eq 'option') {
4052                  if ($self->{open_elements}->[-1]->[1] eq 'option') {
4053                    pop @{$self->{open_elements}};
4054                  } else {
4055                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4056                    ## Ignore the token
4057                  }
4058                  !!!next-token;
4059                  redo B;
4060                } elsif ($token->{tag_name} eq 'select') {
4061                  ## have an element in table scope
4062                  my $i;
4063                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4064                    my $node = $self->{open_elements}->[$_];
4065                    if ($node->[1] eq $token->{tag_name}) {
4066                      $i = $_;
4067                      last INSCOPE;
4068                    } elsif ({
4069                              table => 1, html => 1,
4070                             }->{$node->[1]}) {
4071                      last INSCOPE;
4072                    }
4073                  } # INSCOPE
4074                  unless (defined $i) {
4075                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4076                    ## Ignore the token
4077                    !!!next-token;
4078                    redo B;
4079                  }
4080                  
4081                  splice @{$self->{open_elements}}, $i;
4082    
4083                  $self->_reset_insertion_mode;
4084    
4085                  !!!next-token;
4086                  redo B;
4087                } elsif ({
4088                          caption => 1, table => 1, tbody => 1,
4089                          tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
4090                         }->{$token->{tag_name}}) {
4091                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4092                  
4093                  ## have an element in table scope
4094                  my $i;
4095                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4096                    my $node = $self->{open_elements}->[$_];
4097                    if ($node->[1] eq $token->{tag_name}) {
4098                      $i = $_;
4099                      last INSCOPE;
4100                    } elsif ({
4101                              table => 1, html => 1,
4102                             }->{$node->[1]}) {
4103                      last INSCOPE;
4104                    }
4105                  } # INSCOPE
4106                  unless (defined $i) {
4107                    ## Ignore the token
4108                    !!!next-token;
4109                    redo B;
4110                  }
4111                  
4112                  ## As if </select>
4113                  ## have an element in table scope
4114                  undef $i;
4115                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4116                    my $node = $self->{open_elements}->[$_];
4117                    if ($node->[1] eq 'select') {
4118                      $i = $_;
4119                      last INSCOPE;
4120                    } elsif ({
4121                              table => 1, html => 1,
4122                             }->{$node->[1]}) {
4123                      last INSCOPE;
4124                    }
4125                  } # INSCOPE
4126                  unless (defined $i) {
4127                    !!!parse-error (type => 'unmatched end tag:select');
4128                    ## Ignore the </select> token
4129                    !!!next-token; ## TODO: ok?
4130                    redo B;
4131                  }
4132                  
4133                  splice @{$self->{open_elements}}, $i;
4134    
4135                  $self->_reset_insertion_mode;
4136    
4137                  ## reprocess
4138                  redo B;
4139            } else {
4140              !!!parse-error (type => 'in select:/'.$token->{tag_name});
4141              ## Ignore the token
4142              !!!next-token;
4143              redo B;
4144            }
4145          } else {
4146            die "$0: $token->{type}: Unknown token type";
4147          }
4148        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
4149          if ($token->{type} == CHARACTER_TOKEN) {
4150            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4151              my $data = $1;
4152              ## As if in body
4153              $reconstruct_active_formatting_elements->($insert_to_current);
4154                  
4155              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4156              
4157              unless (length $token->{data}) {
4158                !!!next-token;
4159                redo B;
4160              }
4161            }
4162            
4163            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4164              !!!parse-error (type => 'after html:#character');
4165    
4166              ## Reprocess in the "main" phase, "after body" insertion mode...
4167            }
4168            
4169            ## "after body" insertion mode
4170            !!!parse-error (type => 'after body:#character');
4171    
4172            $self->{insertion_mode} = IN_BODY_IM;
4173            ## reprocess
4174            redo B;
4175          } elsif ($token->{type} == START_TAG_TOKEN) {
4176            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4177              !!!parse-error (type => 'after html:'.$token->{tag_name});
4178              
4179              ## Reprocess in the "main" phase, "after body" insertion mode...
4180            }
4181    
4182            ## "after body" insertion mode
4183            !!!parse-error (type => 'after body:'.$token->{tag_name});
4184    
4185            $self->{insertion_mode} = IN_BODY_IM;
4186            ## reprocess
4187            redo B;
4188          } elsif ($token->{type} == END_TAG_TOKEN) {
4189            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4190              !!!parse-error (type => 'after html:/'.$token->{tag_name});
4191              
4192              $self->{insertion_mode} = AFTER_BODY_IM;
4193              ## Reprocess in the "main" phase, "after body" insertion mode...
4194            }
4195    
4196            ## "after body" insertion mode
4197            if ($token->{tag_name} eq 'html') {
4198              if (defined $self->{inner_html_node}) {
4199                !!!parse-error (type => 'unmatched end tag:html');
4200                ## Ignore the token
4201                !!!next-token;
4202                redo B;
4203              } else {
4204                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
4205                !!!next-token;
4206                redo B;
4207              }
4208            } else {
4209              !!!parse-error (type => 'after body:/'.$token->{tag_name});
4210    
4211              $self->{insertion_mode} = IN_BODY_IM;
4212              ## reprocess
4213              redo B;
4214            }
4215          } else {
4216            die "$0: $token->{type}: Unknown token type";
4217          }
4218        } elsif ($self->{insertion_mode} & FRAME_IMS) {
4219          if ($token->{type} == CHARACTER_TOKEN) {
4220            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4221              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4222              
4223              unless (length $token->{data}) {
4224                !!!next-token;
4225                redo B;
4226              }
4227            }
4228            
4229            if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
4230              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4231                !!!parse-error (type => 'in frameset:#character');
4232              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
4233                !!!parse-error (type => 'after frameset:#character');
4234              } else { # "after html frameset"
4235                !!!parse-error (type => 'after html:#character');
4236    
4237                $self->{insertion_mode} = AFTER_FRAMESET_IM;
4238                ## Reprocess in the "main" phase, "after frameset"...
4239                !!!parse-error (type => 'after frameset:#character');
4240              }
4241              
4242              ## Ignore the token.
4243              if (length $token->{data}) {
4244                ## reprocess the rest of characters
4245              } else {
4246                !!!next-token;
4247              }
4248              redo B;
4249            }
4250            
4251            die qq[$0: Character "$token->{data}"];
4252          } elsif ($token->{type} == START_TAG_TOKEN) {
4253            if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4254              !!!parse-error (type => 'after html:'.$token->{tag_name});
4255    
4256              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4257              ## Process in the "main" phase, "after frameset" insertion mode...
4258            }
4259    
4260            if ($token->{tag_name} eq 'frameset' and
4261                $self->{insertion_mode} == IN_FRAMESET_IM) {
4262              !!!insert-element ($token->{tag_name}, $token->{attributes});
4263              !!!next-token;
4264              redo B;
4265            } elsif ($token->{tag_name} eq 'frame' and
4266                     $self->{insertion_mode} == IN_FRAMESET_IM) {
4267              !!!insert-element ($token->{tag_name}, $token->{attributes});
4268              pop @{$self->{open_elements}};
4269              !!!next-token;
4270              redo B;
4271            } elsif ($token->{tag_name} eq 'noframes') {
4272              ## NOTE: As if in body.
4273              $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);
4274              redo B;
4275            } else {
4276              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4277                !!!parse-error (type => 'in frameset:'.$token->{tag_name});
4278              } else {
4279                !!!parse-error (type => 'after frameset:'.$token->{tag_name});
4280              }
4281              ## Ignore the token
4282              !!!next-token;
4283              redo B;
4284            }
4285          } elsif ($token->{type} == END_TAG_TOKEN) {
4286            if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4287              !!!parse-error (type => 'after html:/'.$token->{tag_name});
4288    
4289              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4290              ## Process in the "main" phase, "after frameset" insertion mode...
4291            }
4292    
4293            if ($token->{tag_name} eq 'frameset' and
4294                $self->{insertion_mode} == IN_FRAMESET_IM) {
4295              if ($self->{open_elements}->[-1]->[1] eq 'html' and
4296                  @{$self->{open_elements}} == 1) {
4297                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4298                ## Ignore the token
4299                !!!next-token;
4300              } else {
4301                pop @{$self->{open_elements}};
4302                !!!next-token;
4303              }
4304    
4305              if (not defined $self->{inner_html_node} and
4306                  $self->{open_elements}->[-1]->[1] ne 'frameset') {
4307                $self->{insertion_mode} = AFTER_FRAMESET_IM;
4308              }
4309              redo B;
4310            } elsif ($token->{tag_name} eq 'html' and
4311                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
4312              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
4313              !!!next-token;
4314              redo B;
4315            } else {
4316              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4317                !!!parse-error (type => 'in frameset:/'.$token->{tag_name});
4318              } else {
4319                !!!parse-error (type => 'after frameset:/'.$token->{tag_name});
4320              }
4321              ## Ignore the token
4322              !!!next-token;
4323              redo B;
4324            }
4325          } else {
4326            die "$0: $token->{type}: Unknown token type";
4327          }
4328    
4329          ## ISSUE: An issue in spec here
4330        } else {
4331          die "$0: $self->{insertion_mode}: Unknown insertion mode";
4332        }
4333    
4334        ## "in body" insertion mode
4335        if ($token->{type} == START_TAG_TOKEN) {
4336        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
4337          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
4338          $script_start_tag->($insert);          $script_start_tag->($insert);
4339          return;          redo B;
4340        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
4341          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
4342          $parse_rcdata->('CDATA', $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4343          return;          redo B;
4344        } elsif ({        } elsif ({
4345                  base => 1, link => 1, meta => 1,                  base => 1, link => 1,
4346                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4347          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
4348          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4349          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4350          !!!next-token;          !!!next-token;
4351          ## TODO: Extracting |charset| from |meta|.          redo B;
4352          return;        } elsif ($token->{tag_name} eq 'meta') {
4353            ## NOTE: This is an "as if in head" code clone, only "-t" differs
4354            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4355            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4356    
4357            unless ($self->{confident}) {
4358              my $charset;
4359              if ($token->{attributes}->{charset}) { ## TODO: And if supported
4360                $charset = $token->{attributes}->{charset}->{value};
4361              }
4362              if ($token->{attributes}->{'http-equiv'}) {
4363                ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
4364                if ($token->{attributes}->{'http-equiv'}->{value}
4365                    =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=
4366                        [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4367                        ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
4368                  $charset = defined $1 ? $1 : defined $2 ? $2 : $3;
4369                } ## TODO: And if supported
4370              }
4371              ## TODO: Change the encoding
4372            }
4373    
4374            !!!next-token;
4375            redo B;
4376        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
4377          !!!parse-error (type => 'in body:title');          !!!parse-error (type => 'in body:title');
4378          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
4379          $parse_rcdata->('RCDATA', sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {
4380            if (defined $self->{head_element}) {            if (defined $self->{head_element}) {
4381              $self->{head_element}->append_child ($_[0]);              $self->{head_element}->append_child ($_[0]);
4382            } else {            } else {
4383              $insert->($_[0]);              $insert->($_[0]);
4384            }            }
4385          });          });
4386          return;          redo B;
4387        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
4388          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body:body');
4389                                
# Line 2533  sub _tree_construction_main ($) { Line 4401  sub _tree_construction_main ($) {
4401            }            }
4402          }          }
4403          !!!next-token;          !!!next-token;
4404          return;          redo B;
4405        } elsif ({        } elsif ({
4406                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
4407                  div => 1, dl => 1, fieldset => 1, listing => 1,                  div => 1, dl => 1, fieldset => 1, listing => 1,
# Line 2544  sub _tree_construction_main ($) { Line 4412  sub _tree_construction_main ($) {
4412          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4413            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4414              !!!back-token;              !!!back-token;
4415              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4416              return;              redo B;
4417            } elsif ({            } elsif ({
4418                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4419                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2557  sub _tree_construction_main ($) { Line 4425  sub _tree_construction_main ($) {
4425          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4426          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre') {
4427            !!!next-token;            !!!next-token;
4428            if ($token->{type} eq 'character') {            if ($token->{type} == CHARACTER_TOKEN) {
4429              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
4430              unless (length $token->{data}) {              unless (length $token->{data}) {
4431                !!!next-token;                !!!next-token;
# Line 2566  sub _tree_construction_main ($) { Line 4434  sub _tree_construction_main ($) {
4434          } else {          } else {
4435            !!!next-token;            !!!next-token;
4436          }          }
4437          return;          redo B;
4438        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
4439          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
4440            !!!parse-error (type => 'in form:form');            !!!parse-error (type => 'in form:form');
4441            ## Ignore the token            ## Ignore the token
4442            !!!next-token;            !!!next-token;
4443            return;            redo B;
4444          } else {          } else {
4445            ## has a p element in scope            ## has a p element in scope
4446            INSCOPE: for (reverse @{$self->{open_elements}}) {            INSCOPE: for (reverse @{$self->{open_elements}}) {
4447              if ($_->[1] eq 'p') {              if ($_->[1] eq 'p') {
4448                !!!back-token;                !!!back-token;
4449                $token = {type => 'end tag', tag_name => 'p'};                $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4450                return;                redo B;
4451              } elsif ({              } elsif ({
4452                        table => 1, caption => 1, td => 1, th => 1,                        table => 1, caption => 1, td => 1, th => 1,
4453                        button => 1, marquee => 1, object => 1, html => 1,                        button => 1, marquee => 1, object => 1, html => 1,
# Line 2591  sub _tree_construction_main ($) { Line 4459  sub _tree_construction_main ($) {
4459            !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4460            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
4461            !!!next-token;            !!!next-token;
4462            return;            redo B;
4463          }          }
4464        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4465          ## has a p element in scope          ## has a p element in scope
4466          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4467            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4468              !!!back-token;              !!!back-token;
4469              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4470              return;              redo B;
4471            } elsif ({            } elsif ({
4472                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4473                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2639  sub _tree_construction_main ($) { Line 4507  sub _tree_construction_main ($) {
4507                        
4508          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4509          !!!next-token;          !!!next-token;
4510          return;          redo B;
4511        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {
4512          ## has a p element in scope          ## has a p element in scope
4513          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4514            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4515              !!!back-token;              !!!back-token;
4516              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4517              return;              redo B;
4518            } elsif ({            } elsif ({
4519                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4520                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2686  sub _tree_construction_main ($) { Line 4554  sub _tree_construction_main ($) {
4554                        
4555          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4556          !!!next-token;          !!!next-token;
4557          return;          redo B;
4558        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
4559          ## has a p element in scope          ## has a p element in scope
4560          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4561            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4562              !!!back-token;              !!!back-token;
4563              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4564              return;              redo B;
4565            } elsif ({            } elsif ({
4566                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4567                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2704  sub _tree_construction_main ($) { Line 4572  sub _tree_construction_main ($) {
4572                        
4573          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4574                        
4575          $self->{content_model_flag} = 'PLAINTEXT';          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
4576                        
4577          !!!next-token;          !!!next-token;
4578          return;          redo B;
4579        } elsif ({        } elsif ({
4580                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
4581                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 2716  sub _tree_construction_main ($) { Line 4584  sub _tree_construction_main ($) {
4584            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4585            if ($node->[1] eq 'p') {            if ($node->[1] eq 'p') {
4586              !!!back-token;              !!!back-token;
4587              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4588              return;              redo B;
4589            } elsif ({            } elsif ({
4590                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4591                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2752  sub _tree_construction_main ($) { Line 4620  sub _tree_construction_main ($) {
4620          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4621                        
4622          !!!next-token;          !!!next-token;
4623          return;          redo B;
4624        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4625          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4626            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
# Line 2760  sub _tree_construction_main ($) { Line 4628  sub _tree_construction_main ($) {
4628              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a');
4629                            
4630              !!!back-token;              !!!back-token;
4631              $token = {type => 'end tag', tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a'};
4632              $formatting_end_tag->($token->{tag_name});              $formatting_end_tag->($token->{tag_name});
4633                            
4634              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
# Line 2787  sub _tree_construction_main ($) { Line 4655  sub _tree_construction_main ($) {
4655          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
4656    
4657          !!!next-token;          !!!next-token;
4658          return;          redo B;
4659        } elsif ({        } elsif ({
4660                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
4661                  s => 1, small => 1, strile => 1,                  s => 1, small => 1, strile => 1,
# Line 2799  sub _tree_construction_main ($) { Line 4667  sub _tree_construction_main ($) {
4667          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
4668                    
4669          !!!next-token;          !!!next-token;
4670          return;          redo B;
4671        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
4672          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
4673    
# Line 2807  sub _tree_construction_main ($) { Line 4675  sub _tree_construction_main ($) {
4675          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4676            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4677            if ($node->[1] eq 'nobr') {            if ($node->[1] eq 'nobr') {
4678              !!!parse-error (type => 'not closed:nobr');              !!!parse-error (type => 'in nobr:nobr');
4679              !!!back-token;              !!!back-token;
4680              $token = {type => 'end tag', tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};
4681              return;              redo B;
4682            } elsif ({            } elsif ({
4683                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4684                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2823  sub _tree_construction_main ($) { Line 4691  sub _tree_construction_main ($) {
4691          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
4692                    
4693          !!!next-token;          !!!next-token;
4694          return;          redo B;
4695        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
4696          ## has a button element in scope          ## has a button element in scope
4697          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 2831  sub _tree_construction_main ($) { Line 4699  sub _tree_construction_main ($) {
4699            if ($node->[1] eq 'button') {            if ($node->[1] eq 'button') {
4700              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button');
4701              !!!back-token;              !!!back-token;
4702              $token = {type => 'end tag', tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button'};
4703              return;              redo B;
4704            } elsif ({            } elsif ({
4705                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4706                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2847  sub _tree_construction_main ($) { Line 4715  sub _tree_construction_main ($) {
4715          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
4716    
4717          !!!next-token;          !!!next-token;
4718          return;          redo B;
4719        } elsif ($token->{tag_name} eq 'marquee' or        } elsif ($token->{tag_name} eq 'marquee' or
4720                 $token->{tag_name} eq 'object') {                 $token->{tag_name} eq 'object') {
4721          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
# Line 2856  sub _tree_construction_main ($) { Line 4724  sub _tree_construction_main ($) {
4724          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
4725                    
4726          !!!next-token;          !!!next-token;
4727          return;          redo B;
4728        } elsif ($token->{tag_name} eq 'xmp') {        } elsif ($token->{tag_name} eq 'xmp') {
4729          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
4730          $parse_rcdata->('CDATA', $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4731          return;          redo B;
4732        } elsif ($token->{tag_name} eq 'table') {        } elsif ($token->{tag_name} eq 'table') {
4733          ## has a p element in scope          ## has a p element in scope
4734          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4735            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4736              !!!back-token;              !!!back-token;
4737              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4738              return;              redo B;
4739            } elsif ({            } elsif ({
4740                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4741                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2878  sub _tree_construction_main ($) { Line 4746  sub _tree_construction_main ($) {
4746                        
4747          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4748                        
4749          $self->{insertion_mode} = 'in table';          $self->{insertion_mode} = IN_TABLE_IM;
4750                        
4751          !!!next-token;          !!!next-token;
4752          return;          redo B;
4753        } elsif ({        } elsif ({
4754                  area => 1, basefont => 1, bgsound => 1, br => 1,                  area => 1, basefont => 1, bgsound => 1, br => 1,
4755                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
# Line 2899  sub _tree_construction_main ($) { Line 4767  sub _tree_construction_main ($) {
4767          pop @{$self->{open_elements}};          pop @{$self->{open_elements}};
4768                    
4769          !!!next-token;          !!!next-token;
4770          return;          redo B;
4771        } elsif ($token->{tag_name} eq 'hr') {        } elsif ($token->{tag_name} eq 'hr') {
4772          ## has a p element in scope          ## has a p element in scope
4773          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4774            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4775              !!!back-token;              !!!back-token;
4776              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4777              return;              redo B;
4778            } elsif ({            } elsif ({
4779                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
4780                      button => 1, marquee => 1, object => 1, html => 1,                      button => 1, marquee => 1, object => 1, html => 1,
# Line 2919  sub _tree_construction_main ($) { Line 4787  sub _tree_construction_main ($) {
4787          pop @{$self->{open_elements}};          pop @{$self->{open_elements}};
4788                        
4789          !!!next-token;          !!!next-token;
4790          return;          redo B;
4791        } elsif ($token->{tag_name} eq 'input') {        } elsif ($token->{tag_name} eq 'input') {
4792          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
4793                    
# Line 2928  sub _tree_construction_main ($) { Line 4796  sub _tree_construction_main ($) {
4796          pop @{$self->{open_elements}};          pop @{$self->{open_elements}};
4797                    
4798          !!!next-token;          !!!next-token;
4799          return;          redo B;
4800        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
4801          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex');
4802                    
4803          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
4804            ## Ignore the token            ## Ignore the token
4805            !!!next-token;            !!!next-token;
4806            return;            redo B;
4807          } else {          } else {
4808            my $at = $token->{attributes};            my $at = $token->{attributes};
4809            my $form_attrs;            my $form_attrs;
# Line 2945  sub _tree_construction_main ($) { Line 4813  sub _tree_construction_main ($) {
4813            delete $at->{action};            delete $at->{action};
4814            delete $at->{prompt};            delete $at->{prompt};
4815            my @tokens = (            my @tokens = (
4816                          {type => 'start tag', tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
4817                           attributes => $form_attrs},                           attributes => $form_attrs},
4818                          {type => 'start tag', tag_name => 'hr'},                          {type => START_TAG_TOKEN, tag_name => 'hr'},
4819                          {type => 'start tag', tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'p'},
4820                          {type => 'start tag', tag_name => 'label'},                          {type => START_TAG_TOKEN, tag_name => 'label'},
4821                         );                         );
4822            if ($prompt_attr) {            if ($prompt_attr) {
4823              push @tokens, {type => 'character', data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};
4824            } else {            } else {
4825              push @tokens, {type => 'character',              push @tokens, {type => CHARACTER_TOKEN,
4826                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD
4827              ## TODO: make this configurable              ## TODO: make this configurable
4828            }            }
4829            push @tokens,            push @tokens,
4830                          {type => 'start tag', tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},
4831                          #{type => 'character', data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4832                          {type => 'end tag', tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label'},
4833                          {type => 'end tag', tag_name => 'p'},                          {type => END_TAG_TOKEN, tag_name => 'p'},
4834                          {type => 'start tag', tag_name => 'hr'},                          {type => START_TAG_TOKEN, tag_name => 'hr'},
4835                          {type => 'end tag', tag_name => 'form'};                          {type => END_TAG_TOKEN, tag_name => 'form'};
4836            $token = shift @tokens;            $token = shift @tokens;
4837            !!!back-token (@tokens);            !!!back-token (@tokens);
4838            return;            redo B;
4839          }          }
4840        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4841          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
# Line 2975  sub _tree_construction_main ($) { Line 4843  sub _tree_construction_main ($) {
4843          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes});
4844                    
4845          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4846          $self->{content_model_flag} = 'RCDATA';          $self->{content_model} = RCDATA_CONTENT_MODEL;
4847          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4848                    
4849          $insert->($el);          $insert->($el);
4850                    
4851          my $text = '';          my $text = '';
4852          !!!next-token;          !!!next-token;
4853          if ($token->{type} eq 'character') {          if ($token->{type} == CHARACTER_TOKEN) {
4854            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
4855            unless (length $token->{data}) {            unless (length $token->{data}) {
4856              !!!next-token;              !!!next-token;
4857            }            }
4858          }          }
4859          while ($token->{type} eq 'character') {          while ($token->{type} == CHARACTER_TOKEN) {
4860            $text .= $token->{data};            $text .= $token->{data};
4861            !!!next-token;            !!!next-token;
4862          }          }
# Line 2996  sub _tree_construction_main ($) { Line 4864  sub _tree_construction_main ($) {
4864            $el->manakai_append_text ($text);            $el->manakai_append_text ($text);
4865          }          }
4866                    
4867          $self->{content_model_flag} = 'PCDATA';          $self->{content_model} = PCDATA_CONTENT_MODEL;
4868                    
4869          if ($token->{type} eq 'end tag' and          if ($token->{type} == END_TAG_TOKEN and
4870              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
4871            ## Ignore the token            ## Ignore the token
4872          } else {          } else {
4873            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!parse-error (type => 'in RCDATA:#'.$token->{type});
4874          }          }
4875          !!!next-token;          !!!next-token;
4876          return;          redo B;
4877        } elsif ({        } elsif ({
4878                  iframe => 1,                  iframe => 1,
4879                  noembed => 1,                  noembed => 1,
4880                  noframes => 1,                  noframes => 1,
4881                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
4882                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4883          $parse_rcdata->('CDATA', $insert);          ## NOTE: There is an "as if in body" code clone.
4884          return;          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4885            redo B;
4886        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'select') {
4887          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
4888                    
4889          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4890                    
4891          $self->{insertion_mode} = 'in select';          $self->{insertion_mode} = IN_SELECT_IM;
4892          !!!next-token;          !!!next-token;
4893          return;          redo B;
4894        } elsif ({        } elsif ({
4895                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4896                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 3031  sub _tree_construction_main ($) { Line 4900  sub _tree_construction_main ($) {
4900          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body:'.$token->{tag_name});
4901          ## Ignore the token          ## Ignore the token
4902          !!!next-token;          !!!next-token;
4903          return;          redo B;
4904                    
4905          ## ISSUE: An issue on HTML5 new elements in the spec.          ## ISSUE: An issue on HTML5 new elements in the spec.
4906        } else {        } else {
# Line 3040  sub _tree_construction_main ($) { Line 4909  sub _tree_construction_main ($) {
4909          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4910                    
4911          !!!next-token;          !!!next-token;
4912          return;          redo B;
4913        }        }
4914      } elsif ($token->{type} eq 'end tag') {      } elsif ($token->{type} == END_TAG_TOKEN) {
4915        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
4916          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
4917              $self->{open_elements}->[1]->[1] eq 'body') {              $self->{open_elements}->[1]->[1] eq 'body') {
# Line 3056  sub _tree_construction_main ($) { Line 4925  sub _tree_construction_main ($) {
4925              }              }
4926            }            }
4927    
4928            $self->{insertion_mode} = 'after body';            $self->{insertion_mode} = AFTER_BODY_IM;
4929            !!!next-token;            !!!next-token;
4930            return;            redo B;
4931          } else {          } else {
4932            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4933            ## Ignore the token            ## Ignore the token
4934            !!!next-token;            !!!next-token;
4935            return;            redo B;
4936          }          }
4937        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
4938          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
# Line 3071  sub _tree_construction_main ($) { Line 4940  sub _tree_construction_main ($) {
4940            if ($self->{open_elements}->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
4941              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);
4942            }            }
4943            $self->{insertion_mode} = 'after body';            $self->{insertion_mode} = AFTER_BODY_IM;
4944            ## reprocess            ## reprocess
4945            return;            redo B;
4946          } else {          } else {
4947            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4948            ## Ignore the token            ## Ignore the token
4949            !!!next-token;            !!!next-token;
4950            return;            redo B;
4951          }          }
4952        } elsif ({        } elsif ({
4953                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
# Line 3103  sub _tree_construction_main ($) { Line 4972  sub _tree_construction_main ($) {
4972                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
4973                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
4974                !!!back-token;                !!!back-token;
4975                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
4976                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4977                return;                redo B;
4978              }              }
4979              $i = $_;              $i = $_;
4980              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE unless $token->{tag_name} eq 'p';
# Line 3118  sub _tree_construction_main ($) { Line 4987  sub _tree_construction_main ($) {
4987          } # INSCOPE          } # INSCOPE
4988                    
4989          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
4990            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            if (defined $i) {
4991                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4992              } else {
4993                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4994              }
4995          }          }
4996                    
4997          if (defined $i) {          if (defined $i) {
# Line 3134  sub _tree_construction_main ($) { Line 5007  sub _tree_construction_main ($) {
5007              button => 1, marquee => 1, object => 1,              button => 1, marquee => 1, object => 1,
5008            }->{$token->{tag_name}};            }->{$token->{tag_name}};
5009          !!!next-token;          !!!next-token;
5010          return;          redo B;
5011        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
5012          ## has an element in scope          ## has an element in scope
5013          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3147  sub _tree_construction_main ($) { Line 5020  sub _tree_construction_main ($) {
5020                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
5021                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5022                !!!back-token;                !!!back-token;
5023                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5024                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5025                return;                redo B;
5026              }              }
5027              last INSCOPE;              last INSCOPE;
5028            } elsif ({            } elsif ({
# Line 3163  sub _tree_construction_main ($) { Line 5036  sub _tree_construction_main ($) {
5036          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {
5037            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
5038          } else {          } else {
5039            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5040          }          }
5041    
5042          undef $self->{form_element};          undef $self->{form_element};
5043          !!!next-token;          !!!next-token;
5044          return;          redo B;
5045        } elsif ({        } elsif ({
5046                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5047                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 3186  sub _tree_construction_main ($) { Line 5059  sub _tree_construction_main ($) {
5059                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
5060                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5061                !!!back-token;                !!!back-token;
5062                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5063                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5064                return;                redo B;
5065              }              }
5066              $i = $_;              $i = $_;
5067              last INSCOPE;              last INSCOPE;
# Line 3201  sub _tree_construction_main ($) { Line 5074  sub _tree_construction_main ($) {
5074          } # INSCOPE          } # INSCOPE
5075                    
5076          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
5077            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5078          }          }
5079                    
5080          splice @{$self->{open_elements}}, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
5081          !!!next-token;          !!!next-token;
5082          return;          redo B;
5083        } elsif ({        } elsif ({
5084                  a => 1,                  a => 1,
5085                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
# Line 3214  sub _tree_construction_main ($) { Line 5087  sub _tree_construction_main ($) {
5087                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
5088                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5089          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token->{tag_name});
5090          return;          redo B;
5091        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
5092          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag:br');
5093    
# Line 3227  sub _tree_construction_main ($) { Line 5100  sub _tree_construction_main ($) {
5100                    
5101          ## Ignore the token.          ## Ignore the token.
5102          !!!next-token;          !!!next-token;
5103          return;          redo B;
5104        } elsif ({        } elsif ({
5105                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
5106                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 3243  sub _tree_construction_main ($) { Line 5116  sub _tree_construction_main ($) {
5116          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5117          ## Ignore the token          ## Ignore the token
5118          !!!next-token;          !!!next-token;
5119          return;          redo B;
5120                    
5121          ## ISSUE: Issue on HTML5 new elements in spec          ## ISSUE: Issue on HTML5 new elements in spec
5122                    
# Line 3260  sub _tree_construction_main ($) { Line 5133  sub _tree_construction_main ($) {
5133              if ({              if ({
5134                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
5135                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
5136                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot => 1, thead => 1,
5137                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5138                !!!back-token;                !!!back-token;
5139                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5140                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5141                return;                redo B;
5142              }              }
5143                    
5144              ## Step 2              ## Step 2
5145              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
5146                  ## NOTE: <x><y></x>
5147                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
5148              }              }
5149                            
# Line 3298  sub _tree_construction_main ($) { Line 5172  sub _tree_construction_main ($) {
5172            ## Step 5;            ## Step 5;
5173            redo S2;            redo S2;
5174          } # S2          } # S2
5175          return;          redo B;
       }  
     }  
   }; # $in_body  
   
   B: {  
     if ($phase eq 'main') {  
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'in html:#DOCTYPE');  
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
 ## ISSUE: "aa<html>" is not a parse error.  
 ## ISSUE: "<html>" in fragment is not a parse error.  
         unless ($token->{first_start_tag}) {  
           !!!parse-error (type => 'not first start tag');  
         }  
         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,  
              tbody => 1, tfoot=> 1, thead => 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]);  
         }  
   
         ## Stop parsing  
         last B;  
   
         ## ISSUE: There is an issue in the spec.  
       } else {  
         if ($self->{insertion_mode} eq 'before 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;  
               }  
             }  
             ## As if <head>  
             !!!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 ({  
                  head => 1, body => 1, html => 1,  
                  p => 1, br => 1,  
                 }->{$token->{tag_name}}) {  
               ## As if <head>  
               !!!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;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token ## ISSUE: An issue in the spec.  
               !!!next-token;  
               redo B;  
             }  
           } else {  
             die "$0: $token->{type}: Unknown type";  
           }  
         } elsif ($self->{insertion_mode} eq 'in head' or  
                  $self->{insertion_mode} eq 'in head noscript' or  
                  $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;  
               }  
             }  
               
             #  
           } 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 ({base => ($self->{insertion_mode} eq 'in head' or  
                           $self->{insertion_mode} eq 'after head'),  
                  link => 1, meta => 1}->{$token->{tag_name}}) {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
               ## TODO: Extracting |charset| from |meta|.  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'title' and  
                      $self->{insertion_mode} eq 'in head') {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               my $parent = defined $self->{head_element} ? $self->{head_element}  
                   : $self->{open_elements}->[-1]->[0];  
               $parse_rcdata->('RCDATA', sub { $parent->append_child ($_[0]) });  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               redo B;  
             } elsif ($token->{tag_name} eq 'style') {  
               ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and  
               ## insertion mode 'in head')  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               $parse_rcdata->('CDATA', $insert_to_current);  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               redo B;  
             } elsif ($token->{tag_name} eq 'noscript') {  
               if ($self->{insertion_mode} eq 'in head') {  
                 ## NOTE: and scripting is disalbed  
                 !!!insert-element ($token->{tag_name}, $token->{attributes});  
                 $self->{insertion_mode} = 'in head noscript';  
                 !!!next-token;  
                 redo B;  
               } elsif ($self->{insertion_mode} eq 'in head noscript') {  
                 !!!parse-error (type => 'in noscript:noscript');  
                 ## Ignore the token  
                 redo B;  
               } else {  
                 #  
               }  
             } elsif ($token->{tag_name} eq 'head' and  
                      $self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'in head:head'); # or in head noscript  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} ne 'in head noscript' and  
                      $token->{tag_name} eq 'script') {  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               ## NOTE: There is a "as if in head" code clone.  
               $script_start_tag->($insert_to_current);  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'after head' and  
                      $token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'after head' and  
                      $token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($self->{insertion_mode} eq 'in head' and  
                 $token->{tag_name} eq 'head') {  
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'after head';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'in head noscript' and  
                 $token->{tag_name} eq 'noscript') {  
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'in head';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'in head' and  
                      {  
                       body => 1, html => 1,  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} eq 'in head noscript' and  
                      {  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if </head> or </noscript> or <body>  
           if ($self->{insertion_mode} eq 'in head') {  
             pop @{$self->{open_elements}};  
             $self->{insertion_mode} = 'after head';  
           } elsif ($self->{insertion_mode} eq 'in head noscript') {  
             pop @{$self->{open_elements}};  
             !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));  
             $self->{insertion_mode} = 'in head';  
           } else { # 'after head'  
             !!!insert-element ('body');  
             $self->{insertion_mode} = 'in body';  
           }  
           ## reprocess  
           redo B;  
   
           ## ISSUE: An issue in the spec.  
         } 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;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             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') {  
             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 ({  
                  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}};  
               }  
   
               push @$active_formatting_elements, ['#marker', '']  
                 if $token->{tag_name} eq 'caption';  
   
               !!!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}};  
               !!!next-token;  
               redo B;  
             } elsif ({  
                       col => 1,  
                       td => 1, th => 1, tr => 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}};  
               }  
   
               !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');  
               $self->{insertion_mode} = $token->{tag_name} eq 'col'  
                 ? 'in column group' : 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
   
               ## As if </table>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'table') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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 'table') {  
               ## have a table 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;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
           $in_body->($insert_to_foster);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in caption') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This 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: 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') {  
             if ({  
                  caption => 1, col => 1, colgroup => 1, tbody => 1,  
                  td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,  
                 }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'not closed:caption');  
   
               ## As if </caption>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'caption') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:caption');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <?>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'caption') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
   
               $self->{insertion_mode} = 'in table';  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'caption') {  
               ## have a table 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;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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 'caption') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
   
               $self->{insertion_mode} = 'in table';  
   
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               !!!parse-error (type => 'not closed:caption');  
   
               ## As if </caption>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'caption') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:caption');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$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;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'caption') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
   
               $self->{insertion_mode} = 'in table';  
   
               ## reprocess  
               redo B;  
             } elsif ({  
                       body => 1, col => 1, colgroup => 1,  
                       html => 1, tbody => 1, td => 1, tfoot => 1,  
                       th => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               redo B;  
             } 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');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               } else {  
                 pop @{$self->{open_elements}}; # colgroup  
                 $self->{insertion_mode} = 'in table';  
                 !!!next-token;  
                 redo B;              
               }  
             } elsif ($token->{tag_name} eq 'col') {  
               !!!parse-error (type => 'unmatched end tag:col');  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if </colgroup>  
           if ($self->{open_elements}->[-1]->[1] eq 'html') {  
             !!!parse-error (type => 'unmatched end tag:colgroup');  
             ## Ignore the token  
             !!!next-token;  
             redo B;  
           } else {  
             pop @{$self->{open_elements}}; # colgroup  
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
           }  
         } 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;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
   
             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 ({  
                  tr => 1,  
                  th => 1, td => 1,  
                 }->{$token->{tag_name}}) {  
               unless ($token->{tag_name} eq 'tr') {  
                 !!!parse-error (type => 'missing start tag:tr');  
               }  
   
               ## 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}};  
               }  
                 
               $self->{insertion_mode} = 'in row';  
               if ($token->{tag_name} eq 'tr') {  
                 !!!insert-element ($token->{tag_name}, $token->{attributes});  
                 !!!next-token;  
               } else {  
                 !!!insert-element ('tr');  
                 ## reprocess  
               }  
               redo B;  
             } elsif ({  
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                      }->{$token->{tag_name}}) {  
               ## 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  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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 ($token->{tag_name} eq 'table') {  
               ## NOTE: This is a code clone of "table in table"  
               !!!parse-error (type => 'not closed:table');  
   
               ## As if </table>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'table') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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 ({  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## 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;  
               }  
   
               ## 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}};  
               }  
   
               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  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
             
           ## As if in table  
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
           $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;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             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>  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'tr') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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}};  
               }  
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: This is a code clone of "table in table"  
               !!!parse-error (type => 'not closed:table');  
   
               ## As if </table>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'table') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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}};  
               }  
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </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 'tr') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{type});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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}};  
               }  
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ({  
                       tbody => 1, tfoot => 1, thead => 1,  
                      }->{$token->{tag_name}}) {  
               ## 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;  
               }  
   
               ## As if </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 'tr') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:tr');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## 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}};  
               }  
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if in table  
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
           $in_body->($insert_to_foster);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in cell') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This 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: 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') {  
             if ({  
                  caption => 1, col => 1, colgroup => 1,  
                  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  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Close the cell  
               !!!back-token; # <?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {  
               ## 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;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => ($token->{tag_name} eq 'th'),  
                    th => ($token->{tag_name} eq 'td'),  
                    tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 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});  
               ## Ignore the token  
               !!!next-token;  
               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;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
             
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($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 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @{$self->{open_elements}};  
               }  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'optgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @{$self->{open_elements}};  
               }  
   
               if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {  
                 ## As if </optgroup>  
                 pop @{$self->{open_elements}};  
               }  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               !!!parse-error (type => 'not closed:select');  
               ## As if </select> instead  
               ## 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:select');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'optgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option' and  
                   $self->{open_elements}->[-2]->[1] eq 'optgroup') {  
                 ## As if </option>  
                 splice @{$self->{open_elements}}, -2;  
               } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {  
                 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;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       caption => 1, table => 1, tbody => 1,  
                       tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 
               ## 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) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## As if </select>  
               ## have an element in table scope  
               undef $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'select') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the </select> token  
                 !!!next-token; ## TODO: ok?  
                 redo B;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           !!!parse-error (type => 'in select:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'after body') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               ## As if in body  
               $reconstruct_active_formatting_elements->($insert_to_current);  
                 
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
             !!!parse-error (type => 'after body:#'.$token->{type});  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[0]->[0]->append_child ($comment);  
             !!!next-token;  
             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});  
             }  
           } else {  
             !!!parse-error (type => 'after body:#'.$token->{type});  
           }  
   
           $self->{insertion_mode} = 'in body';  
           ## reprocess  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in frameset') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
               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 'frameset') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frame') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             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 {  
             #  
           }  
             
           if (defined $token->{tag_name}) {  
             !!!parse-error (type => 'in frameset:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});  
           } else {  
             !!!parse-error (type => 'in frameset:#'.$token->{type});  
           }  
           ## Ignore the token  
           !!!next-token;  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'after frameset') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
               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 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               $phase = 'trailing end';  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
             
           if (defined $token->{tag_name}) {  
             !!!parse-error (type => 'after frameset:'.($token->{tag_name} eq 'end tag' ? '/' : '').$token->{tag_name});  
           } else {  
             !!!parse-error (type => 'after frameset:#'.$token->{type});  
           }  
           ## Ignore the token  
           !!!next-token;  
           redo B;  
   
           ## ISSUE: An issue in spec there  
         } else {  
           die "$0: $self->{insertion_mode}: Unknown insertion mode";  
         }  
       }  
     } elsif ($phase eq 'trailing end') {  
       ## states in the main stage is preserved yet # MUST  
         
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'after html:#DOCTYPE');  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'comment') {  
         my $comment = $self->{document}->create_comment ($token->{data});  
         $self->{document}->append_child ($comment);  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'character') {  
         if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
           my $data = $1;  
           ## As if in the main phase.  
           ## NOTE: The insertion mode in the main phase  
           ## just before the phase has been changed to the trailing  
           ## end phase is either "after body" or "after frameset".  
           $reconstruct_active_formatting_elements->($insert_to_current)  
             if $phase eq 'main';  
             
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);  
             
           unless (length $token->{data}) {  
             !!!next-token;  
             redo B;  
           }  
         }  
   
         !!!parse-error (type => 'after html:#character');  
         $phase = 'main';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'start tag' or  
                $token->{type} eq 'end tag') {  
         !!!parse-error (type => 'after html:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});  
         $phase = 'main';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Stop parsing  
         last B;  
       } else {  
         die "$0: $token->{type}: Unknown token";  
5176        }        }
5177      }      }
5178        redo B;
5179    } # B    } # B
5180    
5181      ## NOTE: The "trailing end" phase in HTML5 is split into
5182      ## two insertion modes: "after html body" and "after html frameset".
5183      ## NOTE: States in the main stage is preserved while
5184      ## the parser stays in the trailing end phase. # MUST
5185    
5186    ## Stop parsing # MUST    ## Stop parsing # MUST
5187        
5188    ## TODO: script stuffs    ## TODO: script stuffs
# Line 5276  sub set_inner_html ($$$) { Line 5267  sub set_inner_html ($$$) {
5267    
5268      ## Step 2      ## Step 2
5269      my $node_ln = $node->local_name;      my $node_ln = $node->local_name;
5270      $p->{content_model_flag} = {      $p->{content_model} = {
5271        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
5272        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
5273        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
5274        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
5275        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
5276        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
5277        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
5278        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
5279        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
5280        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
5281      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
5282         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
5283            unless defined $p->{content_model};
5284            ## ISSUE: What is "the name of the element"? local name?
5285    
5286      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $node_ln];
5287    

Legend:
Removed from v.1.31  
changed lines
  Added in v.1.59

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24