/[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.76 by wakaba, Mon Mar 3 09:17:10 2008 UTC revision 1.124 by wakaba, Sun Apr 6 10:34:11 2008 UTC
# Line 12  use Error qw(:try); Line 12  use Error qw(:try);
12  ## TODO: 1252 parse error (revision 1264)  ## TODO: 1252 parse error (revision 1264)
13  ## TODO: 8859-11 = 874 (revision 1271)  ## TODO: 8859-11 = 874 (revision 1271)
14    
15    sub A_EL () { 0b1 }
16    sub ADDRESS_EL () { 0b10 }
17    sub BODY_EL () { 0b100 }
18    sub BUTTON_EL () { 0b1000 }
19    sub CAPTION_EL () { 0b10000 }
20    sub DD_EL () { 0b100000 }
21    sub DIV_EL () { 0b1000000 }
22    sub DT_EL () { 0b10000000 }
23    sub FORM_EL () { 0b100000000 }
24    sub FORMATTING_EL () { 0b1000000000 }
25    sub FRAMESET_EL () { 0b10000000000 }
26    sub HEADING_EL () { 0b100000000000 }
27    sub HTML_EL () { 0b1000000000000 }
28    sub LI_EL () { 0b10000000000000 }
29    sub NOBR_EL () { 0b100000000000000 }
30    sub OPTION_EL () { 0b1000000000000000 }
31    sub OPTGROUP_EL () { 0b10000000000000000 }
32    sub P_EL () { 0b100000000000000000 }
33    sub SELECT_EL () { 0b1000000000000000000 }
34    sub TABLE_EL () { 0b10000000000000000000 }
35    sub TABLE_CELL_EL () { 0b100000000000000000000 }
36    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
37    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
38    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
39    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
40    
41    sub TABLE_ROWS_EL () {
42      TABLE_EL |
43      TABLE_ROW_EL |
44      TABLE_ROW_GROUP_EL
45    }
46    
47    sub END_TAG_OPTIONAL_EL () {
48      DD_EL |
49      DT_EL |
50      LI_EL |
51      P_EL
52    }
53    
54    sub ALL_END_TAG_OPTIONAL_EL () {
55      END_TAG_OPTIONAL_EL |
56      BODY_EL |
57      HTML_EL |
58      TABLE_CELL_EL |
59      TABLE_ROW_EL |
60      TABLE_ROW_GROUP_EL
61    }
62    
63    sub SCOPING_EL () {
64      BUTTON_EL |
65      CAPTION_EL |
66      HTML_EL |
67      TABLE_EL |
68      TABLE_CELL_EL |
69      MISC_SCOPING_EL
70    }
71    
72    sub TABLE_SCOPING_EL () {
73      HTML_EL |
74      TABLE_EL
75    }
76    
77    sub TABLE_ROWS_SCOPING_EL () {
78      HTML_EL |
79      TABLE_ROW_GROUP_EL
80    }
81    
82    sub TABLE_ROW_SCOPING_EL () {
83      HTML_EL |
84      TABLE_ROW_EL
85    }
86    
87    sub SPECIAL_EL () {
88      ADDRESS_EL |
89      BODY_EL |
90      DIV_EL |
91      END_TAG_OPTIONAL_EL |
92      FORM_EL |
93      FRAMESET_EL |
94      HEADING_EL |
95      OPTION_EL |
96      OPTGROUP_EL |
97      SELECT_EL |
98      TABLE_ROW_EL |
99      TABLE_ROW_GROUP_EL |
100      MISC_SPECIAL_EL
101    }
102    
103    my $el_category = {
104      a => A_EL | FORMATTING_EL,
105      address => ADDRESS_EL,
106      applet => MISC_SCOPING_EL,
107      area => MISC_SPECIAL_EL,
108      b => FORMATTING_EL,
109      base => MISC_SPECIAL_EL,
110      basefont => MISC_SPECIAL_EL,
111      bgsound => MISC_SPECIAL_EL,
112      big => FORMATTING_EL,
113      blockquote => MISC_SPECIAL_EL,
114      body => BODY_EL,
115      br => MISC_SPECIAL_EL,
116      button => BUTTON_EL,
117      caption => CAPTION_EL,
118      center => MISC_SPECIAL_EL,
119      col => MISC_SPECIAL_EL,
120      colgroup => MISC_SPECIAL_EL,
121      dd => DD_EL,
122      dir => MISC_SPECIAL_EL,
123      div => DIV_EL,
124      dl => MISC_SPECIAL_EL,
125      dt => DT_EL,
126      em => FORMATTING_EL,
127      embed => MISC_SPECIAL_EL,
128      fieldset => MISC_SPECIAL_EL,
129      font => FORMATTING_EL,
130      form => FORM_EL,
131      frame => MISC_SPECIAL_EL,
132      frameset => FRAMESET_EL,
133      h1 => HEADING_EL,
134      h2 => HEADING_EL,
135      h3 => HEADING_EL,
136      h4 => HEADING_EL,
137      h5 => HEADING_EL,
138      h6 => HEADING_EL,
139      head => MISC_SPECIAL_EL,
140      hr => MISC_SPECIAL_EL,
141      html => HTML_EL,
142      i => FORMATTING_EL,
143      iframe => MISC_SPECIAL_EL,
144      img => MISC_SPECIAL_EL,
145      input => MISC_SPECIAL_EL,
146      isindex => MISC_SPECIAL_EL,
147      li => LI_EL,
148      link => MISC_SPECIAL_EL,
149      listing => MISC_SPECIAL_EL,
150      marquee => MISC_SCOPING_EL,
151      menu => MISC_SPECIAL_EL,
152      meta => MISC_SPECIAL_EL,
153      nobr => NOBR_EL | FORMATTING_EL,
154      noembed => MISC_SPECIAL_EL,
155      noframes => MISC_SPECIAL_EL,
156      noscript => MISC_SPECIAL_EL,
157      object => MISC_SCOPING_EL,
158      ol => MISC_SPECIAL_EL,
159      optgroup => OPTGROUP_EL,
160      option => OPTION_EL,
161      p => P_EL,
162      param => MISC_SPECIAL_EL,
163      plaintext => MISC_SPECIAL_EL,
164      pre => MISC_SPECIAL_EL,
165      s => FORMATTING_EL,
166      script => MISC_SPECIAL_EL,
167      select => SELECT_EL,
168      small => FORMATTING_EL,
169      spacer => MISC_SPECIAL_EL,
170      strike => FORMATTING_EL,
171      strong => FORMATTING_EL,
172      style => MISC_SPECIAL_EL,
173      table => TABLE_EL,
174      tbody => TABLE_ROW_GROUP_EL,
175      td => TABLE_CELL_EL,
176      textarea => MISC_SPECIAL_EL,
177      tfoot => TABLE_ROW_GROUP_EL,
178      th => TABLE_CELL_EL,
179      thead => TABLE_ROW_GROUP_EL,
180      title => MISC_SPECIAL_EL,
181      tr => TABLE_ROW_EL,
182      tt => FORMATTING_EL,
183      u => FORMATTING_EL,
184      ul => MISC_SPECIAL_EL,
185      wbr => MISC_SPECIAL_EL,
186    };
187    
188  my $permitted_slash_tag_name = {  my $permitted_slash_tag_name = {
189    base => 1,    base => 1,
190    link => 1,    link => 1,
# Line 61  my $c1_entity_char = { Line 234  my $c1_entity_char = {
234    0x9F => 0x0178,    0x9F => 0x0178,
235  }; # $c1_entity_char  }; # $c1_entity_char
236    
 my $special_category = {  
   address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
   
237  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
238    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
239    my $charset = shift;    my $charset = shift;
# Line 108  sub parse_byte_string ($$$$;$) { Line 259  sub parse_byte_string ($$$$;$) {
259    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
260      my $self = shift;      my $self = shift;
261      my $charset = lc shift;      my $charset = lc shift;
262        my $token = shift;
263      ## TODO: if $charset is supported      ## TODO: if $charset is supported
264      ## TODO: normalize charset name      ## TODO: normalize charset name
265    
# Line 126  sub parse_byte_string ($$$$;$) { Line 278  sub parse_byte_string ($$$$;$) {
278      }      }
279    
280      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.
281          ':'.$charset, level => 'w');          ':'.$charset, level => 'w', token => $token);
282    
283      ## Step 3      ## Step 3
284      # if (can) {      # if (can) {
# Line 177  sub parse_string ($$$;$) { Line 329  sub parse_string ($$$;$) {
329        if defined $self->{input_encoding};        if defined $self->{input_encoding};
330    
331    my $i = 0;    my $i = 0;
332    my $line = 1;    $self->{line_prev} = $self->{line} = 1;
333    my $column = 0;    $self->{column_prev} = $self->{column} = 0;
334    $self->{set_next_char} = sub {    $self->{set_next_char} = sub {
335      my $self = shift;      my $self = shift;
336    
# Line 187  sub parse_string ($$$;$) { Line 339  sub parse_string ($$$;$) {
339    
340      $self->{next_char} = -1 and return if $i >= length $$s;      $self->{next_char} = -1 and return if $i >= length $$s;
341      $self->{next_char} = ord substr $$s, $i++, 1;      $self->{next_char} = ord substr $$s, $i++, 1;
342      $column++;  
343        ($self->{line_prev}, $self->{column_prev})
344            = ($self->{line}, $self->{column});
345        $self->{column}++;
346            
347      if ($self->{next_char} == 0x000A) { # LF      if ($self->{next_char} == 0x000A) { # LF
348        $line++;        $self->{line}++;
349        $column = 0;        $self->{column} = 0;
350      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{next_char} == 0x000D) { # CR
351        $i++ if substr ($$s, $i, 1) eq "\x0A";        $i++ if substr ($$s, $i, 1) eq "\x0A";
352        $self->{next_char} = 0x000A; # LF # MUST        $self->{next_char} = 0x000A; # LF # MUST
353        $line++;        $self->{line}++;
354        $column = 0;        $self->{column} = 0;
355      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{next_char} > 0x10FFFF) {
356        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
357      } elsif ($self->{next_char} == 0x0000) { # NULL      } elsif ($self->{next_char} == 0x0000) { # NULL
# Line 209  sub parse_string ($$$;$) { Line 364  sub parse_string ($$$;$) {
364    
365    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
366      my (%opt) = @_;      my (%opt) = @_;
367      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
368        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
369        warn "Parse error ($opt{type}) at line $line column $column\n";
370    };    };
371    $self->{parse_error} = sub {    $self->{parse_error} = sub {
372      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
373    };    };
374    
375    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 220  sub parse_string ($$$;$) { Line 377  sub parse_string ($$$;$) {
377    $self->_construct_tree;    $self->_construct_tree;
378    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
379    
380      delete $self->{parse_error}; # remove loop
381    
382    return $self->{document};    return $self->{document};
383  } # parse_string  } # parse_string
384    
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 462  sub TABLE_IMS ()      { 0b1000000 }
462  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
463  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
464  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
465    sub SELECT_IMS ()     { 0b10000000000 }
466    
467    ## NOTE: "initial" and "before html" insertion modes have no constants.
468    
469    ## NOTE: "after after body" insertion mode.
470  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
471    
472    ## NOTE: "after after frameset" insertion mode.
473  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
474    
475  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
476  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
477  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 319  sub IN_TABLE_IM () { TABLE_IMS } Line 485  sub IN_TABLE_IM () { TABLE_IMS }
485  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
486  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
487  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
488  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
489    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
490  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
491    
492  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 387  sub _get_next_token ($) { Line 554  sub _get_next_token ($) {
554        if ($self->{next_char} == 0x0026) { # &        if ($self->{next_char} == 0x0026) { # &
555          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
556              not $self->{escape}) {              not $self->{escape}) {
557              !!!cp (1);
558            $self->{state} = ENTITY_DATA_STATE;            $self->{state} = ENTITY_DATA_STATE;
559            !!!next-input-character;            !!!next-input-character;
560            redo A;            redo A;
561          } else {          } else {
562              !!!cp (2);
563            #            #
564          }          }
565        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
# Line 399  sub _get_next_token ($) { Line 568  sub _get_next_token ($) {
568              if ($self->{prev_char}->[0] == 0x002D and # -              if ($self->{prev_char}->[0] == 0x002D and # -
569                  $self->{prev_char}->[1] == 0x0021 and # !                  $self->{prev_char}->[1] == 0x0021 and # !
570                  $self->{prev_char}->[2] == 0x003C) { # <                  $self->{prev_char}->[2] == 0x003C) { # <
571                  !!!cp (3);
572                $self->{escape} = 1;                $self->{escape} = 1;
573                } else {
574                  !!!cp (4);
575              }              }
576              } else {
577                !!!cp (5);
578            }            }
579          }          }
580                    
# Line 409  sub _get_next_token ($) { Line 583  sub _get_next_token ($) {
583          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
584              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
585               not $self->{escape})) {               not $self->{escape})) {
586              !!!cp (6);
587            $self->{state} = TAG_OPEN_STATE;            $self->{state} = TAG_OPEN_STATE;
588            !!!next-input-character;            !!!next-input-character;
589            redo A;            redo A;
590          } else {          } else {
591              !!!cp (7);
592            #            #
593          }          }
594        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
# Line 420  sub _get_next_token ($) { Line 596  sub _get_next_token ($) {
596              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
597            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{prev_char}->[0] == 0x002D and # -
598                $self->{prev_char}->[1] == 0x002D) { # -                $self->{prev_char}->[1] == 0x002D) { # -
599                !!!cp (8);
600              delete $self->{escape};              delete $self->{escape};
601              } else {
602                !!!cp (9);
603            }            }
604            } else {
605              !!!cp (10);
606          }          }
607                    
608          #          #
609        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
610          !!!emit ({type => END_OF_FILE_TOKEN});          !!!cp (11);
611            !!!emit ({type => END_OF_FILE_TOKEN,
612                      line => $self->{line}, column => $self->{column}});
613          last A; ## TODO: ok?          last A; ## TODO: ok?
614          } else {
615            !!!cp (12);
616        }        }
617        # Anything else        # Anything else
618        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
619                     data => chr $self->{next_char}};                     data => chr $self->{next_char},
620                       line => $self->{line}, column => $self->{column},
621                      };
622        ## Stay in the data state        ## Stay in the data state
623        !!!next-input-character;        !!!next-input-character;
624    
# Line 440  sub _get_next_token ($) { Line 627  sub _get_next_token ($) {
627        redo A;        redo A;
628      } elsif ($self->{state} == ENTITY_DATA_STATE) {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
629        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
630    
631          my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
632                
633        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);
634    
# Line 447  sub _get_next_token ($) { Line 636  sub _get_next_token ($) {
636        # next-input-character is already done        # next-input-character is already done
637    
638        unless (defined $token) {        unless (defined $token) {
639          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          !!!cp (13);
640            !!!emit ({type => CHARACTER_TOKEN, data => '&',
641                      line => $l, column => $c,
642                     });
643        } else {        } else {
644            !!!cp (14);
645          !!!emit ($token);          !!!emit ($token);
646        }        }
647    
# Line 456  sub _get_next_token ($) { Line 649  sub _get_next_token ($) {
649      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
650        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
651          if ($self->{next_char} == 0x002F) { # /          if ($self->{next_char} == 0x002F) { # /
652              !!!cp (15);
653            !!!next-input-character;            !!!next-input-character;
654            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
655            redo A;            redo A;
656          } else {          } else {
657              !!!cp (16);
658            ## reconsume            ## reconsume
659            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
660    
661            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
662                        line => $self->{line_prev},
663                        column => $self->{column_prev},
664                       });
665    
666            redo A;            redo A;
667          }          }
668        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
669          if ($self->{next_char} == 0x0021) { # !          if ($self->{next_char} == 0x0021) { # !
670              !!!cp (17);
671            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
672            !!!next-input-character;            !!!next-input-character;
673            redo A;            redo A;
674          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{next_char} == 0x002F) { # /
675              !!!cp (18);
676            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
677            !!!next-input-character;            !!!next-input-character;
678            redo A;            redo A;
679          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{next_char} and
680                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{next_char} <= 0x005A) { # A..Z
681              !!!cp (19);
682            $self->{current_token}            $self->{current_token}
683              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
684                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{next_char} + 0x0020),
685                   line => $self->{line_prev},
686                   column => $self->{column_prev}};
687            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
688            !!!next-input-character;            !!!next-input-character;
689            redo A;            redo A;
690          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{next_char} and
691                   $self->{next_char} <= 0x007A) { # a..z                   $self->{next_char} <= 0x007A) { # a..z
692              !!!cp (20);
693            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{current_token} = {type => START_TAG_TOKEN,
694                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{next_char}),
695                                        line => $self->{line_prev},
696                                        column => $self->{column_prev}};
697            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
698            !!!next-input-character;            !!!next-input-character;
699            redo A;            redo A;
700          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{next_char} == 0x003E) { # >
701            !!!parse-error (type => 'empty start tag');            !!!cp (21);
702              !!!parse-error (type => 'empty start tag',
703                              line => $self->{line_prev},
704                              column => $self->{column_prev});
705            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
706            !!!next-input-character;            !!!next-input-character;
707    
708            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
709                        line => $self->{line_prev},
710                        column => $self->{column_prev},
711                       });
712    
713            redo A;            redo A;
714          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{next_char} == 0x003F) { # ?
715            !!!parse-error (type => 'pio');            !!!cp (22);
716              !!!parse-error (type => 'pio',
717                              line => $self->{line_prev},
718                              column => $self->{column_prev});
719            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
720              $self->{current_token} = {type => COMMENT_TOKEN, data => '',
721                                        line => $self->{line_prev},
722                                        column => $self->{column_prev},
723                                       };
724            ## $self->{next_char} is intentionally left as is            ## $self->{next_char} is intentionally left as is
725            redo A;            redo A;
726          } else {          } else {
727              !!!cp (23);
728            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago');
729            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
730            ## reconsume            ## reconsume
731    
732            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
733                        line => $self->{line_prev},
734                        column => $self->{column_prev},
735                       });
736    
737            redo A;            redo A;
738          }          }
# Line 517  sub _get_next_token ($) { Line 740  sub _get_next_token ($) {
740          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
741        }        }
742      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
743          my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
744        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
745          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_emitted_start_tag_name}) {
746    
747            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
748            my @next_char;            my @next_char;
749            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {
# Line 526  sub _get_next_token ($) { Line 751  sub _get_next_token ($) {
751              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);
752              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;
753              if ($self->{next_char} == $c or $self->{next_char} == $C) {              if ($self->{next_char} == $c or $self->{next_char} == $C) {
754                  !!!cp (24);
755                !!!next-input-character;                !!!next-input-character;
756                next TAGNAME;                next TAGNAME;
757              } else {              } else {
758                  !!!cp (25);
759                $self->{next_char} = shift @next_char; # reconsume                $self->{next_char} = shift @next_char; # reconsume
760                !!!back-next-input-character (@next_char);                !!!back-next-input-character (@next_char);
761                $self->{state} = DATA_STATE;                $self->{state} = DATA_STATE;
762    
763                !!!emit ({type => CHARACTER_TOKEN, data => '</'});                !!!emit ({type => CHARACTER_TOKEN, data => '</',
764                            line => $l, column => $c,
765                           });
766        
767                redo A;                redo A;
768              }              }
# Line 548  sub _get_next_token ($) { Line 777  sub _get_next_token ($) {
777                    $self->{next_char} == 0x003E or # >                    $self->{next_char} == 0x003E or # >
778                    $self->{next_char} == 0x002F or # /                    $self->{next_char} == 0x002F or # /
779                    $self->{next_char} == -1) {                    $self->{next_char} == -1) {
780                !!!cp (26);
781              $self->{next_char} = shift @next_char; # reconsume              $self->{next_char} = shift @next_char; # reconsume
782              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
783              $self->{state} = DATA_STATE;              $self->{state} = DATA_STATE;
784              !!!emit ({type => CHARACTER_TOKEN, data => '</'});              !!!emit ({type => CHARACTER_TOKEN, data => '</',
785                          line => $l, column => $c,
786                         });
787              redo A;              redo A;
788            } else {            } else {
789                !!!cp (27);
790              $self->{next_char} = shift @next_char;              $self->{next_char} = shift @next_char;
791              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
792              # and consume...              # and consume...
793            }            }
794          } else {          } else {
795            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
796              !!!cp (28);
797            # next-input-character is already done            # next-input-character is already done
798            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
799            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            !!!emit ({type => CHARACTER_TOKEN, data => '</',
800                        line => $l, column => $c,
801                       });
802            redo A;            redo A;
803          }          }
804        }        }
805                
806        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{next_char} and
807            $self->{next_char} <= 0x005A) { # A..Z            $self->{next_char} <= 0x005A) { # A..Z
808          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (29);
809                            tag_name => chr ($self->{next_char} + 0x0020)};          $self->{current_token}
810                = {type => END_TAG_TOKEN,
811                   tag_name => chr ($self->{next_char} + 0x0020),
812                   line => $l, column => $c};
813          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
814          !!!next-input-character;          !!!next-input-character;
815          redo A;          redo A;
816        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{next_char} and
817                 $self->{next_char} <= 0x007A) { # a..z                 $self->{next_char} <= 0x007A) { # a..z
818            !!!cp (30);
819          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{current_token} = {type => END_TAG_TOKEN,
820                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{next_char}),
821                                      line => $l, column => $c};
822          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
823          !!!next-input-character;          !!!next-input-character;
824          redo A;          redo A;
825        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
826          !!!parse-error (type => 'empty end tag');          !!!cp (31);
827            !!!parse-error (type => 'empty end tag',
828                            line => $self->{line_prev}, ## "<" in "</>"
829                            column => $self->{column_prev} - 1);
830          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
831          !!!next-input-character;          !!!next-input-character;
832          redo A;          redo A;
833        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
834            !!!cp (32);
835          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
836          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
837          # reconsume          # reconsume
838    
839          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
840                      line => $l, column => $c,
841                     });
842    
843          redo A;          redo A;
844        } else {        } else {
845            !!!cp (33);
846          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
847          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
848            $self->{current_token} = {type => COMMENT_TOKEN, data => '',
849                                      line => $self->{line_prev}, # "<" of "</"
850                                      column => $self->{column_prev} - 1,
851                                     };
852          ## $self->{next_char} is intentionally left as is          ## $self->{next_char} is intentionally left as is
853          redo A;          redo A;
854        }        }
# Line 606  sub _get_next_token ($) { Line 858  sub _get_next_token ($) {
858            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
859            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
860            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
861            !!!cp (34);
862          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
863          !!!next-input-character;          !!!next-input-character;
864          redo A;          redo A;
865        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
866          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
867            $self->{current_token}->{first_start_tag}            !!!cp (35);
               = not defined $self->{last_emitted_start_tag_name};  
868            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
869          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
870            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
871            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
872              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
873            }            #  !!! cp (36);
874              #  !!! parse-error (type => 'end tag attribute');
875              #} else {
876                !!!cp (37);
877              #}
878          } else {          } else {
879            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
880          }          }
# Line 630  sub _get_next_token ($) { Line 886  sub _get_next_token ($) {
886          redo A;          redo A;
887        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
888                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
889            !!!cp (38);
890          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);
891            # start tag or end tag            # start tag or end tag
892          ## Stay in this state          ## Stay in this state
# Line 638  sub _get_next_token ($) { Line 895  sub _get_next_token ($) {
895        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
896          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
897          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
898            $self->{current_token}->{first_start_tag}            !!!cp (39);
               = not defined $self->{last_emitted_start_tag_name};  
899            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
900          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
901            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
902            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
903              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
904            }            #  !!! cp (40);
905              #  !!! parse-error (type => 'end tag attribute');
906              #} else {
907                !!!cp (41);
908              #}
909          } else {          } else {
910            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
911          }          }
# Line 661  sub _get_next_token ($) { Line 921  sub _get_next_token ($) {
921              $self->{current_token}->{type} == START_TAG_TOKEN and              $self->{current_token}->{type} == START_TAG_TOKEN and
922              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
923            # permitted slash            # permitted slash
924              !!!cp (42);
925            #            #
926          } else {          } else {
927              !!!cp (43);
928            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
929          }          }
930          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
931          # next-input-character is already done          # next-input-character is already done
932          redo A;          redo A;
933        } else {        } else {
934            !!!cp (44);
935          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{current_token}->{tag_name} .= chr $self->{next_char};
936            # start tag or end tag            # start tag or end tag
937          ## Stay in the state          ## Stay in the state
# Line 681  sub _get_next_token ($) { Line 944  sub _get_next_token ($) {
944            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
945            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
946            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
947            !!!cp (45);
948          ## Stay in the state          ## Stay in the state
949          !!!next-input-character;          !!!next-input-character;
950          redo A;          redo A;
951        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
952          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
953            $self->{current_token}->{first_start_tag}            !!!cp (46);
               = not defined $self->{last_emitted_start_tag_name};  
954            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
955          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
956            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
957            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
958                !!!cp (47);
959              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
960              } else {
961                !!!cp (48);
962            }            }
963          } else {          } else {
964            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 705  sub _get_next_token ($) { Line 971  sub _get_next_token ($) {
971          redo A;          redo A;
972        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
973                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
974          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          !!!cp (49);
975                                value => ''};          $self->{current_attribute}
976                = {name => chr ($self->{next_char} + 0x0020),
977                   value => '',
978                   line => $self->{line}, column => $self->{column}};
979          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
980          !!!next-input-character;          !!!next-input-character;
981          redo A;          redo A;
# Line 716  sub _get_next_token ($) { Line 985  sub _get_next_token ($) {
985              $self->{current_token}->{type} == START_TAG_TOKEN and              $self->{current_token}->{type} == START_TAG_TOKEN and
986              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
987            # permitted slash            # permitted slash
988              !!!cp (50);
989            #            #
990          } else {          } else {
991              !!!cp (51);
992            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
993          }          }
994          ## Stay in the state          ## Stay in the state
# Line 726  sub _get_next_token ($) { Line 997  sub _get_next_token ($) {
997        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
998          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
999          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1000            $self->{current_token}->{first_start_tag}            !!!cp (52);
               = not defined $self->{last_emitted_start_tag_name};  
1001            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1002          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1003            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1004            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1005                !!!cp (53);
1006              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1007              } else {
1008                !!!cp (54);
1009            }            }
1010          } else {          } else {
1011            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 749  sub _get_next_token ($) { Line 1022  sub _get_next_token ($) {
1022               0x0027 => 1, # '               0x0027 => 1, # '
1023               0x003D => 1, # =               0x003D => 1, # =
1024              }->{$self->{next_char}}) {              }->{$self->{next_char}}) {
1025              !!!cp (55);
1026            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1027            } else {
1028              !!!cp (56);
1029          }          }
1030          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{current_attribute}
1031                                value => ''};              = {name => chr ($self->{next_char}),
1032                   value => '',
1033                   line => $self->{line}, column => $self->{column}};
1034          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1035          !!!next-input-character;          !!!next-input-character;
1036          redo A;          redo A;
# Line 761  sub _get_next_token ($) { Line 1039  sub _get_next_token ($) {
1039        my $before_leave = sub {        my $before_leave = sub {
1040          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
1041              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
1042            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1043              !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
1044            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
1045          } else {          } else {
1046              !!!cp (58);
1047            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
1048              = $self->{current_attribute};              = $self->{current_attribute};
1049          }          }
# Line 774  sub _get_next_token ($) { Line 1054  sub _get_next_token ($) {
1054            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1055            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1056            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1057            !!!cp (59);
1058          $before_leave->();          $before_leave->();
1059          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1060          !!!next-input-character;          !!!next-input-character;
1061          redo A;          redo A;
1062        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1063            !!!cp (60);
1064          $before_leave->();          $before_leave->();
1065          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1066          !!!next-input-character;          !!!next-input-character;
# Line 786  sub _get_next_token ($) { Line 1068  sub _get_next_token ($) {
1068        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1069          $before_leave->();          $before_leave->();
1070          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1071            $self->{current_token}->{first_start_tag}            !!!cp (61);
               = not defined $self->{last_emitted_start_tag_name};  
1072            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1073          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1074              !!!cp (62);
1075            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1076            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1077              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 805  sub _get_next_token ($) { Line 1087  sub _get_next_token ($) {
1087          redo A;          redo A;
1088        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
1089                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1090            !!!cp (63);
1091          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);
1092          ## Stay in the state          ## Stay in the state
1093          !!!next-input-character;          !!!next-input-character;
# Line 816  sub _get_next_token ($) { Line 1099  sub _get_next_token ($) {
1099              $self->{current_token}->{type} == START_TAG_TOKEN and              $self->{current_token}->{type} == START_TAG_TOKEN and
1100              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1101            # permitted slash            # permitted slash
1102              !!!cp (64);
1103            #            #
1104          } else {          } else {
1105              !!!cp (65);
1106            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
1107          }          }
1108          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
# Line 827  sub _get_next_token ($) { Line 1112  sub _get_next_token ($) {
1112          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1113          $before_leave->();          $before_leave->();
1114          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1115            $self->{current_token}->{first_start_tag}            !!!cp (66);
               = not defined $self->{last_emitted_start_tag_name};  
1116            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1117          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1118            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1119            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1120                !!!cp (67);
1121              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1122              } else {
1123                ## NOTE: This state should never be reached.
1124                !!!cp (68);
1125            }            }
1126          } else {          } else {
1127            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 847  sub _get_next_token ($) { Line 1135  sub _get_next_token ($) {
1135        } else {        } else {
1136          if ($self->{next_char} == 0x0022 or # "          if ($self->{next_char} == 0x0022 or # "
1137              $self->{next_char} == 0x0027) { # '              $self->{next_char} == 0x0027) { # '
1138              !!!cp (69);
1139            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1140            } else {
1141              !!!cp (70);
1142          }          }
1143          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{current_attribute}->{name} .= chr ($self->{next_char});
1144          ## Stay in the state          ## Stay in the state
# Line 860  sub _get_next_token ($) { Line 1151  sub _get_next_token ($) {
1151            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1152            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1153            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1154            !!!cp (71);
1155          ## Stay in the state          ## Stay in the state
1156          !!!next-input-character;          !!!next-input-character;
1157          redo A;          redo A;
1158        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1159            !!!cp (72);
1160          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1161          !!!next-input-character;          !!!next-input-character;
1162          redo A;          redo A;
1163        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1164          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1165            $self->{current_token}->{first_start_tag}            !!!cp (73);
               = not defined $self->{last_emitted_start_tag_name};  
1166            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1167          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1168            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1169            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1170                !!!cp (74);
1171              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1172              } else {
1173                ## NOTE: This state should never be reached.
1174                !!!cp (75);
1175            }            }
1176          } else {          } else {
1177            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 888  sub _get_next_token ($) { Line 1184  sub _get_next_token ($) {
1184          redo A;          redo A;
1185        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
1186                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1187          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          !!!cp (76);
1188                                value => ''};          $self->{current_attribute}
1189                = {name => chr ($self->{next_char} + 0x0020),
1190                   value => '',
1191                   line => $self->{line}, column => $self->{column}};
1192          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1193          !!!next-input-character;          !!!next-input-character;
1194          redo A;          redo A;
# Line 899  sub _get_next_token ($) { Line 1198  sub _get_next_token ($) {
1198              $self->{current_token}->{type} == START_TAG_TOKEN and              $self->{current_token}->{type} == START_TAG_TOKEN and
1199              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1200            # permitted slash            # permitted slash
1201              !!!cp (77);
1202            #            #
1203          } else {          } else {
1204              !!!cp (78);
1205            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
1206            ## TODO: Different error type for <aa / bb> than <aa/>            ## TODO: Different error type for <aa / bb> than <aa/>
1207          }          }
# Line 910  sub _get_next_token ($) { Line 1211  sub _get_next_token ($) {
1211        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1212          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1213          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1214            $self->{current_token}->{first_start_tag}            !!!cp (79);
               = not defined $self->{last_emitted_start_tag_name};  
1215            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1216          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1217            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1218            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1219                !!!cp (80);
1220              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1221              } else {
1222                ## NOTE: This state should never be reached.
1223                !!!cp (81);
1224            }            }
1225          } else {          } else {
1226            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 928  sub _get_next_token ($) { Line 1232  sub _get_next_token ($) {
1232    
1233          redo A;          redo A;
1234        } else {        } else {
1235          $self->{current_attribute} = {name => chr ($self->{next_char}),          !!!cp (82);
1236                                value => ''};          $self->{current_attribute}
1237                = {name => chr ($self->{next_char}),
1238                   value => '',
1239                   line => $self->{line}, column => $self->{column}};
1240          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1241          !!!next-input-character;          !!!next-input-character;
1242          redo A;                  redo A;        
# Line 940  sub _get_next_token ($) { Line 1247  sub _get_next_token ($) {
1247            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1248            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1249            $self->{next_char} == 0x0020) { # SP                  $self->{next_char} == 0x0020) { # SP      
1250            !!!cp (83);
1251          ## Stay in the state          ## Stay in the state
1252          !!!next-input-character;          !!!next-input-character;
1253          redo A;          redo A;
1254        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
1255            !!!cp (84);
1256          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1257          !!!next-input-character;          !!!next-input-character;
1258          redo A;          redo A;
1259        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1260            !!!cp (85);
1261          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1262          ## reconsume          ## reconsume
1263          redo A;          redo A;
1264        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
1265            !!!cp (86);
1266          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1267          !!!next-input-character;          !!!next-input-character;
1268          redo A;          redo A;
1269        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1270          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1271            $self->{current_token}->{first_start_tag}            !!!cp (87);
               = not defined $self->{last_emitted_start_tag_name};  
1272            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1273          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1274            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1275            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1276                !!!cp (88);
1277              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1278              } else {
1279                ## NOTE: This state should never be reached.
1280                !!!cp (89);
1281            }            }
1282          } else {          } else {
1283            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 977  sub _get_next_token ($) { Line 1291  sub _get_next_token ($) {
1291        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1292          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1293          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1294            $self->{current_token}->{first_start_tag}            !!!cp (90);
               = not defined $self->{last_emitted_start_tag_name};  
1295            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1296          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1297            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1298            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1299                !!!cp (91);
1300              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1301              } else {
1302                ## NOTE: This state should never be reached.
1303                !!!cp (92);
1304            }            }
1305          } else {          } else {
1306            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 996  sub _get_next_token ($) { Line 1313  sub _get_next_token ($) {
1313          redo A;          redo A;
1314        } else {        } else {
1315          if ($self->{next_char} == 0x003D) { # =          if ($self->{next_char} == 0x003D) { # =
1316              !!!cp (93);
1317            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1318            } else {
1319              !!!cp (94);
1320          }          }
1321          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{current_attribute}->{value} .= chr ($self->{next_char});
1322          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
# Line 1005  sub _get_next_token ($) { Line 1325  sub _get_next_token ($) {
1325        }        }
1326      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1327        if ($self->{next_char} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
1328            !!!cp (95);
1329          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1330          !!!next-input-character;          !!!next-input-character;
1331          redo A;          redo A;
1332        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1333            !!!cp (96);
1334          $self->{last_attribute_value_state} = $self->{state};          $self->{last_attribute_value_state} = $self->{state};
1335          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1336          !!!next-input-character;          !!!next-input-character;
# Line 1016  sub _get_next_token ($) { Line 1338  sub _get_next_token ($) {
1338        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1339          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1340          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1341            $self->{current_token}->{first_start_tag}            !!!cp (97);
               = not defined $self->{last_emitted_start_tag_name};  
1342            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1343          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1344            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1345            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1346                !!!cp (98);
1347              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1348              } else {
1349                ## NOTE: This state should never be reached.
1350                !!!cp (99);
1351            }            }
1352          } else {          } else {
1353            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1034  sub _get_next_token ($) { Line 1359  sub _get_next_token ($) {
1359    
1360          redo A;          redo A;
1361        } else {        } else {
1362            !!!cp (100);
1363          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{current_attribute}->{value} .= chr ($self->{next_char});
1364          ## Stay in the state          ## Stay in the state
1365          !!!next-input-character;          !!!next-input-character;
# Line 1041  sub _get_next_token ($) { Line 1367  sub _get_next_token ($) {
1367        }        }
1368      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1369        if ($self->{next_char} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
1370            !!!cp (101);
1371          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1372          !!!next-input-character;          !!!next-input-character;
1373          redo A;          redo A;
1374        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1375            !!!cp (102);
1376          $self->{last_attribute_value_state} = $self->{state};          $self->{last_attribute_value_state} = $self->{state};
1377          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1378          !!!next-input-character;          !!!next-input-character;
# Line 1052  sub _get_next_token ($) { Line 1380  sub _get_next_token ($) {
1380        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1381          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1382          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1383            $self->{current_token}->{first_start_tag}            !!!cp (103);
               = not defined $self->{last_emitted_start_tag_name};  
1384            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1385          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1386            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1387            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1388                !!!cp (104);
1389              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1390              } else {
1391                ## NOTE: This state should never be reached.
1392                !!!cp (105);
1393            }            }
1394          } else {          } else {
1395            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1070  sub _get_next_token ($) { Line 1401  sub _get_next_token ($) {
1401    
1402          redo A;          redo A;
1403        } else {        } else {
1404            !!!cp (106);
1405          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{current_attribute}->{value} .= chr ($self->{next_char});
1406          ## Stay in the state          ## Stay in the state
1407          !!!next-input-character;          !!!next-input-character;
# Line 1081  sub _get_next_token ($) { Line 1413  sub _get_next_token ($) {
1413            $self->{next_char} == 0x000B or # HT            $self->{next_char} == 0x000B or # HT
1414            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1415            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1416            !!!cp (107);
1417          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1418          !!!next-input-character;          !!!next-input-character;
1419          redo A;          redo A;
1420        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1421            !!!cp (108);
1422          $self->{last_attribute_value_state} = $self->{state};          $self->{last_attribute_value_state} = $self->{state};
1423          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1424          !!!next-input-character;          !!!next-input-character;
1425          redo A;          redo A;
1426        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1427          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1428            $self->{current_token}->{first_start_tag}            !!!cp (109);
               = not defined $self->{last_emitted_start_tag_name};  
1429            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1430          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1431            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1432            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1433                !!!cp (110);
1434              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1435              } else {
1436                ## NOTE: This state should never be reached.
1437                !!!cp (111);
1438            }            }
1439          } else {          } else {
1440            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1111  sub _get_next_token ($) { Line 1448  sub _get_next_token ($) {
1448        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1451            $self->{current_token}->{first_start_tag}            !!!cp (112);
               = not defined $self->{last_emitted_start_tag_name};  
1452            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1453          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1454            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1456                !!!cp (113);
1457              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1458              } else {
1459                ## NOTE: This state should never be reached.
1460                !!!cp (114);
1461            }            }
1462          } else {          } else {
1463            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1134  sub _get_next_token ($) { Line 1474  sub _get_next_token ($) {
1474               0x0027 => 1, # '               0x0027 => 1, # '
1475               0x003D => 1, # =               0x003D => 1, # =
1476              }->{$self->{next_char}}) {              }->{$self->{next_char}}) {
1477              !!!cp (115);
1478            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1479            } else {
1480              !!!cp (116);
1481          }          }
1482          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{current_attribute}->{value} .= chr ($self->{next_char});
1483          ## Stay in the state          ## Stay in the state
# Line 1151  sub _get_next_token ($) { Line 1494  sub _get_next_token ($) {
1494             -1);             -1);
1495    
1496        unless (defined $token) {        unless (defined $token) {
1497            !!!cp (117);
1498          $self->{current_attribute}->{value} .= '&';          $self->{current_attribute}->{value} .= '&';
1499        } else {        } else {
1500            !!!cp (118);
1501          $self->{current_attribute}->{value} .= $token->{data};          $self->{current_attribute}->{value} .= $token->{data};
1502          $self->{current_attribute}->{has_reference} = $token->{has_reference};          $self->{current_attribute}->{has_reference} = $token->{has_reference};
1503          ## ISSUE: spec says "append the returned character token to the current attribute's value"          ## ISSUE: spec says "append the returned character token to the current attribute's value"
# Line 1167  sub _get_next_token ($) { Line 1512  sub _get_next_token ($) {
1512            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1513            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1514            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1515            !!!cp (118);
1516          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1517          !!!next-input-character;          !!!next-input-character;
1518          redo A;          redo A;
1519        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1520          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1521            $self->{current_token}->{first_start_tag}            !!!cp (119);
               = not defined $self->{last_emitted_start_tag_name};  
1522            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1523          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1526                !!!cp (120);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                ## NOTE: This state should never be reached.
1530                !!!cp (121);
1531            }            }
1532          } else {          } else {
1533            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1195  sub _get_next_token ($) { Line 1544  sub _get_next_token ($) {
1544              $self->{current_token}->{type} == START_TAG_TOKEN and              $self->{current_token}->{type} == START_TAG_TOKEN and
1545              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
1546            # permitted slash            # permitted slash
1547              !!!cp (122);
1548            #            #
1549          } else {          } else {
1550              !!!cp (123);
1551            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
1552          }          }
1553          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1554          # next-input-character is already done          # next-input-character is already done
1555          redo A;          redo A;
1556        } else {        } else {
1557            !!!cp (124);
1558          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
1559          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1560          ## reconsume          ## reconsume
# Line 1211  sub _get_next_token ($) { Line 1563  sub _get_next_token ($) {
1563      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1564        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1565                
1566        my $token = {type => COMMENT_TOKEN, data => ''};        ## NOTE: Set by the previous state
1567          #my $token = {type => COMMENT_TOKEN, data => ''};
1568    
1569        BC: {        BC: {
1570          if ($self->{next_char} == 0x003E) { # >          if ($self->{next_char} == 0x003E) { # >
1571              !!!cp (124);
1572            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1573            !!!next-input-character;            !!!next-input-character;
1574    
1575            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1576    
1577            redo A;            redo A;
1578          } elsif ($self->{next_char} == -1) {          } elsif ($self->{next_char} == -1) {
1579              !!!cp (125);
1580            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1581            ## reconsume            ## reconsume
1582    
1583            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1584    
1585            redo A;            redo A;
1586          } else {          } else {
1587            $token->{data} .= chr ($self->{next_char});            !!!cp (126);
1588              $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1589            !!!next-input-character;            !!!next-input-character;
1590            redo BC;            redo BC;
1591          }          }
1592        } # BC        } # BC
1593    
1594          die "$0: _get_next_token: unexpected case [BC]";
1595      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1596        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1597    
1598          my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);
1599    
1600        my @next_char;        my @next_char;
1601        push @next_char, $self->{next_char};        push @next_char, $self->{next_char};
1602                
# Line 1244  sub _get_next_token ($) { Line 1604  sub _get_next_token ($) {
1604          !!!next-input-character;          !!!next-input-character;
1605          push @next_char, $self->{next_char};          push @next_char, $self->{next_char};
1606          if ($self->{next_char} == 0x002D) { # -          if ($self->{next_char} == 0x002D) { # -
1607            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};            !!!cp (127);
1608              $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1609                                        line => $l, column => $c,
1610                                       };
1611            $self->{state} = COMMENT_START_STATE;            $self->{state} = COMMENT_START_STATE;
1612            !!!next-input-character;            !!!next-input-character;
1613            redo A;            redo A;
1614            } else {
1615              !!!cp (128);
1616          }          }
1617        } elsif ($self->{next_char} == 0x0044 or # D        } elsif ($self->{next_char} == 0x0044 or # D
1618                 $self->{next_char} == 0x0064) { # d                 $self->{next_char} == 0x0064) { # d
# Line 1275  sub _get_next_token ($) { Line 1640  sub _get_next_token ($) {
1640                    push @next_char, $self->{next_char};                    push @next_char, $self->{next_char};
1641                    if ($self->{next_char} == 0x0045 or # E                    if ($self->{next_char} == 0x0045 or # E
1642                        $self->{next_char} == 0x0065) { # e                        $self->{next_char} == 0x0065) { # e
1643                      ## ISSUE: What a stupid code this is!                      !!!cp (129);
1644                        ## TODO: What a stupid code this is!
1645                      $self->{state} = DOCTYPE_STATE;                      $self->{state} = DOCTYPE_STATE;
1646                        $self->{current_token} = {type => DOCTYPE_TOKEN,
1647                                                  quirks => 1,
1648                                                  line => $l, column => $c,
1649                                                 };
1650                      !!!next-input-character;                      !!!next-input-character;
1651                      redo A;                      redo A;
1652                      } else {
1653                        !!!cp (130);
1654                    }                    }
1655                    } else {
1656                      !!!cp (131);
1657                  }                  }
1658                  } else {
1659                    !!!cp (132);
1660                }                }
1661                } else {
1662                  !!!cp (133);
1663              }              }
1664              } else {
1665                !!!cp (134);
1666            }            }
1667            } else {
1668              !!!cp (135);
1669          }          }
1670          } else {
1671            !!!cp (136);
1672        }        }
1673    
1674        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment');
1675        $self->{next_char} = shift @next_char;        $self->{next_char} = shift @next_char;
1676        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1677        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
1678          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1679                                    line => $l, column => $c,
1680                                   };
1681        redo A;        redo A;
1682                
1683        ## ISSUE: typos in spec: chacacters, is is a parse error        ## ISSUE: typos in spec: chacacters, is is a parse error
1684        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?
1685      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
1686        if ($self->{next_char} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1687            !!!cp (137);
1688          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
1689          !!!next-input-character;          !!!next-input-character;
1690          redo A;          redo A;
1691        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1692            !!!cp (138);
1693          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1694          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1695          !!!next-input-character;          !!!next-input-character;
# Line 1309  sub _get_next_token ($) { Line 1698  sub _get_next_token ($) {
1698    
1699          redo A;          redo A;
1700        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1701            !!!cp (139);
1702          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1703          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1704          ## reconsume          ## reconsume
# Line 1317  sub _get_next_token ($) { Line 1707  sub _get_next_token ($) {
1707    
1708          redo A;          redo A;
1709        } else {        } else {
1710            !!!cp (140);
1711          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1712              .= chr ($self->{next_char});              .= chr ($self->{next_char});
1713          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
# Line 1325  sub _get_next_token ($) { Line 1716  sub _get_next_token ($) {
1716        }        }
1717      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
1718        if ($self->{next_char} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1719            !!!cp (141);
1720          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
1721          !!!next-input-character;          !!!next-input-character;
1722          redo A;          redo A;
1723        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1724            !!!cp (142);
1725          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1726          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1727          !!!next-input-character;          !!!next-input-character;
# Line 1337  sub _get_next_token ($) { Line 1730  sub _get_next_token ($) {
1730    
1731          redo A;          redo A;
1732        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1733            !!!cp (143);
1734          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1735          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1736          ## reconsume          ## reconsume
# Line 1345  sub _get_next_token ($) { Line 1739  sub _get_next_token ($) {
1739    
1740          redo A;          redo A;
1741        } else {        } else {
1742            !!!cp (144);
1743          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1744              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{next_char});
1745          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
# Line 1353  sub _get_next_token ($) { Line 1748  sub _get_next_token ($) {
1748        }        }
1749      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
1750        if ($self->{next_char} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1751            !!!cp (145);
1752          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
1753          !!!next-input-character;          !!!next-input-character;
1754          redo A;          redo A;
1755        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1756            !!!cp (146);
1757          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1758          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1759          ## reconsume          ## reconsume
# Line 1365  sub _get_next_token ($) { Line 1762  sub _get_next_token ($) {
1762    
1763          redo A;          redo A;
1764        } else {        } else {
1765            !!!cp (147);
1766          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1767          ## Stay in the state          ## Stay in the state
1768          !!!next-input-character;          !!!next-input-character;
# Line 1372  sub _get_next_token ($) { Line 1770  sub _get_next_token ($) {
1770        }        }
1771      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
1772        if ($self->{next_char} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
1773            !!!cp (148);
1774          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
1775          !!!next-input-character;          !!!next-input-character;
1776          redo A;          redo A;
1777        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1778            !!!cp (149);
1779          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1780          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1781          ## reconsume          ## reconsume
# Line 1384  sub _get_next_token ($) { Line 1784  sub _get_next_token ($) {
1784    
1785          redo A;          redo A;
1786        } else {        } else {
1787            !!!cp (150);
1788          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
1789          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
1790          !!!next-input-character;          !!!next-input-character;
# Line 1391  sub _get_next_token ($) { Line 1792  sub _get_next_token ($) {
1792        }        }
1793      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
1794        if ($self->{next_char} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
1795            !!!cp (151);
1796          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1797          !!!next-input-character;          !!!next-input-character;
1798    
# Line 1398  sub _get_next_token ($) { Line 1800  sub _get_next_token ($) {
1800    
1801          redo A;          redo A;
1802        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
1803          !!!parse-error (type => 'dash in comment');          !!!cp (152);
1804            !!!parse-error (type => 'dash in comment',
1805                            line => $self->{line_prev},
1806                            column => $self->{column_prev});
1807          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
1808          ## Stay in the state          ## Stay in the state
1809          !!!next-input-character;          !!!next-input-character;
1810          redo A;          redo A;
1811        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1812            !!!cp (153);
1813          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1814          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1815          ## reconsume          ## reconsume
# Line 1412  sub _get_next_token ($) { Line 1818  sub _get_next_token ($) {
1818    
1819          redo A;          redo A;
1820        } else {        } else {
1821          !!!parse-error (type => 'dash in comment');          !!!cp (154);
1822            !!!parse-error (type => 'dash in comment',
1823                            line => $self->{line_prev},
1824                            column => $self->{column_prev});
1825          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
1826          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
1827          !!!next-input-character;          !!!next-input-character;
# Line 1424  sub _get_next_token ($) { Line 1833  sub _get_next_token ($) {
1833            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1834            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1835            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1836            !!!cp (155);
1837          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1838          !!!next-input-character;          !!!next-input-character;
1839          redo A;          redo A;
1840        } else {        } else {
1841            !!!cp (156);
1842          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
1843          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1844          ## reconsume          ## reconsume
# Line 1439  sub _get_next_token ($) { Line 1850  sub _get_next_token ($) {
1850            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1851            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1852            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1853            !!!cp (157);
1854          ## Stay in the state          ## Stay in the state
1855          !!!next-input-character;          !!!next-input-character;
1856          redo A;          redo A;
1857        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1858            !!!cp (158);
1859          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1860          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1861          !!!next-input-character;          !!!next-input-character;
1862    
1863          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1864    
1865          redo A;          redo A;
1866        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1867            !!!cp (159);
1868          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1869          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1870          ## reconsume          ## reconsume
1871    
1872          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1873    
1874          redo A;          redo A;
1875        } else {        } else {
1876          $self->{current_token}          !!!cp (160);
1877              = {type => DOCTYPE_TOKEN,          $self->{current_token}->{name} = chr $self->{next_char};
1878                 name => chr ($self->{next_char}),          delete $self->{current_token}->{quirks};
                #quirks => 0,  
               };  
1879  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
1880          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
1881          !!!next-input-character;          !!!next-input-character;
# Line 1476  sub _get_next_token ($) { Line 1888  sub _get_next_token ($) {
1888            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1889            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1890            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1891            !!!cp (161);
1892          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
1893          !!!next-input-character;          !!!next-input-character;
1894          redo A;          redo A;
1895        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1896            !!!cp (162);
1897          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1898          !!!next-input-character;          !!!next-input-character;
1899    
# Line 1487  sub _get_next_token ($) { Line 1901  sub _get_next_token ($) {
1901    
1902          redo A;          redo A;
1903        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1904            !!!cp (163);
1905          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1906          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
# Line 1496  sub _get_next_token ($) { Line 1911  sub _get_next_token ($) {
1911    
1912          redo A;          redo A;
1913        } else {        } else {
1914            !!!cp (164);
1915          $self->{current_token}->{name}          $self->{current_token}->{name}
1916            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{next_char}); # DOCTYPE
1917          ## Stay in the state          ## Stay in the state
# Line 1508  sub _get_next_token ($) { Line 1924  sub _get_next_token ($) {
1924            $self->{next_char} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1925            $self->{next_char} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1926            $self->{next_char} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1927            !!!cp (165);
1928          ## Stay in the state          ## Stay in the state
1929          !!!next-input-character;          !!!next-input-character;
1930          redo A;          redo A;
1931        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1932            !!!cp (166);
1933          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1934          !!!next-input-character;          !!!next-input-character;
1935    
# Line 1519  sub _get_next_token ($) { Line 1937  sub _get_next_token ($) {
1937    
1938          redo A;          redo A;
1939        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1940            !!!cp (167);
1941          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1942          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1943          ## reconsume          ## reconsume
# Line 1544  sub _get_next_token ($) { Line 1963  sub _get_next_token ($) {
1963                  !!!next-input-character;                  !!!next-input-character;
1964                  if ($self->{next_char} == 0x0043 or # C                  if ($self->{next_char} == 0x0043 or # C
1965                      $self->{next_char} == 0x0063) { # c                      $self->{next_char} == 0x0063) { # c
1966                      !!!cp (168);
1967                    $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;                    $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1968                    !!!next-input-character;                    !!!next-input-character;
1969                    redo A;                    redo A;
1970                    } else {
1971                      !!!cp (169);
1972                  }                  }
1973                  } else {
1974                    !!!cp (170);
1975                }                }
1976                } else {
1977                  !!!cp (171);
1978              }              }
1979              } else {
1980                !!!cp (172);
1981            }            }
1982            } else {
1983              !!!cp (173);
1984          }          }
1985    
1986          #          #
# Line 1571  sub _get_next_token ($) { Line 2001  sub _get_next_token ($) {
2001                  !!!next-input-character;                  !!!next-input-character;
2002                  if ($self->{next_char} == 0x004D or # M                  if ($self->{next_char} == 0x004D or # M
2003                      $self->{next_char} == 0x006D) { # m                      $self->{next_char} == 0x006D) { # m
2004                      !!!cp (174);
2005                    $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;                    $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2006                    !!!next-input-character;                    !!!next-input-character;
2007                    redo A;                    redo A;
2008                    } else {
2009                      !!!cp (175);
2010                  }                  }
2011                  } else {
2012                    !!!cp (176);
2013                }                }
2014                } else {
2015                  !!!cp (177);
2016              }              }
2017              } else {
2018                !!!cp (178);
2019            }            }
2020            } else {
2021              !!!cp (179);
2022          }          }
2023    
2024          #          #
2025        } else {        } else {
2026            !!!cp (180);
2027          !!!next-input-character;          !!!next-input-character;
2028          #          #
2029        }        }
# Line 1597  sub _get_next_token ($) { Line 2039  sub _get_next_token ($) {
2039              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2040              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2041            }->{$self->{next_char}}) {            }->{$self->{next_char}}) {
2042            !!!cp (181);
2043          ## Stay in the state          ## Stay in the state
2044          !!!next-input-character;          !!!next-input-character;
2045          redo A;          redo A;
2046        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{next_char} eq 0x0022) { # "
2047            !!!cp (182);
2048          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2049          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2050          !!!next-input-character;          !!!next-input-character;
2051          redo A;          redo A;
2052        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{next_char} eq 0x0027) { # '
2053            !!!cp (183);
2054          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2055          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2056          !!!next-input-character;          !!!next-input-character;
2057          redo A;          redo A;
2058        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{next_char} eq 0x003E) { # >
2059            !!!cp (184);
2060          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2061    
2062          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1621  sub _get_next_token ($) { Line 2067  sub _get_next_token ($) {
2067    
2068          redo A;          redo A;
2069        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2070            !!!cp (185);
2071          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2072    
2073          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1631  sub _get_next_token ($) { Line 2078  sub _get_next_token ($) {
2078    
2079          redo A;          redo A;
2080        } else {        } else {
2081            !!!cp (186);
2082          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2083          $self->{current_token}->{quirks} = 1;          $self->{current_token}->{quirks} = 1;
2084    
# Line 1640  sub _get_next_token ($) { Line 2088  sub _get_next_token ($) {
2088        }        }
2089      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2090        if ($self->{next_char} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
2091            !!!cp (187);
2092          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2093          !!!next-input-character;          !!!next-input-character;
2094          redo A;          redo A;
2095        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2096            !!!cp (188);
2097          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2098    
2099          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1654  sub _get_next_token ($) { Line 2104  sub _get_next_token ($) {
2104    
2105          redo A;          redo A;
2106        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2107            !!!cp (189);
2108          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2109    
2110          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1664  sub _get_next_token ($) { Line 2115  sub _get_next_token ($) {
2115    
2116          redo A;          redo A;
2117        } else {        } else {
2118            !!!cp (190);
2119          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{current_token}->{public_identifier} # DOCTYPE
2120              .= chr $self->{next_char};              .= chr $self->{next_char};
2121          ## Stay in the state          ## Stay in the state
# Line 1672  sub _get_next_token ($) { Line 2124  sub _get_next_token ($) {
2124        }        }
2125      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2126        if ($self->{next_char} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
2127            !!!cp (191);
2128          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2129          !!!next-input-character;          !!!next-input-character;
2130          redo A;          redo A;
2131        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2132            !!!cp (192);
2133          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2134    
2135          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1686  sub _get_next_token ($) { Line 2140  sub _get_next_token ($) {
2140    
2141          redo A;          redo A;
2142        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2143            !!!cp (193);
2144          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2145    
2146          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1696  sub _get_next_token ($) { Line 2151  sub _get_next_token ($) {
2151    
2152          redo A;          redo A;
2153        } else {        } else {
2154            !!!cp (194);
2155          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{current_token}->{public_identifier} # DOCTYPE
2156              .= chr $self->{next_char};              .= chr $self->{next_char};
2157          ## Stay in the state          ## Stay in the state
# Line 1707  sub _get_next_token ($) { Line 2163  sub _get_next_token ($) {
2163              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2164              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2165            }->{$self->{next_char}}) {            }->{$self->{next_char}}) {
2166            !!!cp (195);
2167          ## Stay in the state          ## Stay in the state
2168          !!!next-input-character;          !!!next-input-character;
2169          redo A;          redo A;
2170        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
2171            !!!cp (196);
2172          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2173          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2174          !!!next-input-character;          !!!next-input-character;
2175          redo A;          redo A;
2176        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
2177            !!!cp (197);
2178          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2179          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2180          !!!next-input-character;          !!!next-input-character;
2181          redo A;          redo A;
2182        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2183            !!!cp (198);
2184          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2185          !!!next-input-character;          !!!next-input-character;
2186    
# Line 1728  sub _get_next_token ($) { Line 2188  sub _get_next_token ($) {
2188    
2189          redo A;          redo A;
2190        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2191            !!!cp (199);
2192          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2193    
2194          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1738  sub _get_next_token ($) { Line 2199  sub _get_next_token ($) {
2199    
2200          redo A;          redo A;
2201        } else {        } else {
2202            !!!cp (200);
2203          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2204          $self->{current_token}->{quirks} = 1;          $self->{current_token}->{quirks} = 1;
2205    
# Line 1750  sub _get_next_token ($) { Line 2212  sub _get_next_token ($) {
2212              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2213              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2214            }->{$self->{next_char}}) {            }->{$self->{next_char}}) {
2215            !!!cp (201);
2216          ## Stay in the state          ## Stay in the state
2217          !!!next-input-character;          !!!next-input-character;
2218          redo A;          redo A;
2219        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
2220            !!!cp (202);
2221          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2222          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2223          !!!next-input-character;          !!!next-input-character;
2224          redo A;          redo A;
2225        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
2226            !!!cp (203);
2227          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2228          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2229          !!!next-input-character;          !!!next-input-character;
2230          redo A;          redo A;
2231        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2232            !!!cp (204);
2233          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2234          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2235          !!!next-input-character;          !!!next-input-character;
# Line 1773  sub _get_next_token ($) { Line 2239  sub _get_next_token ($) {
2239    
2240          redo A;          redo A;
2241        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2242            !!!cp (205);
2243          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2244    
2245          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1783  sub _get_next_token ($) { Line 2250  sub _get_next_token ($) {
2250    
2251          redo A;          redo A;
2252        } else {        } else {
2253            !!!cp (206);
2254          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2255          $self->{current_token}->{quirks} = 1;          $self->{current_token}->{quirks} = 1;
2256    
# Line 1792  sub _get_next_token ($) { Line 2260  sub _get_next_token ($) {
2260        }        }
2261      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2262        if ($self->{next_char} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
2263            !!!cp (207);
2264          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2265          !!!next-input-character;          !!!next-input-character;
2266          redo A;          redo A;
2267        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2268            !!!cp (208);
2269          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2270    
2271          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1806  sub _get_next_token ($) { Line 2276  sub _get_next_token ($) {
2276    
2277          redo A;          redo A;
2278        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2279            !!!cp (209);
2280          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2281    
2282          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1816  sub _get_next_token ($) { Line 2287  sub _get_next_token ($) {
2287    
2288          redo A;          redo A;
2289        } else {        } else {
2290            !!!cp (210);
2291          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{current_token}->{system_identifier} # DOCTYPE
2292              .= chr $self->{next_char};              .= chr $self->{next_char};
2293          ## Stay in the state          ## Stay in the state
# Line 1824  sub _get_next_token ($) { Line 2296  sub _get_next_token ($) {
2296        }        }
2297      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2298        if ($self->{next_char} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
2299            !!!cp (211);
2300          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2304            !!!cp (212);
2305          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2306    
2307          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1838  sub _get_next_token ($) { Line 2312  sub _get_next_token ($) {
2312    
2313          redo A;          redo A;
2314        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2315            !!!cp (213);
2316          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2317    
2318          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1848  sub _get_next_token ($) { Line 2323  sub _get_next_token ($) {
2323    
2324          redo A;          redo A;
2325        } else {        } else {
2326            !!!cp (214);
2327          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{current_token}->{system_identifier} # DOCTYPE
2328              .= chr $self->{next_char};              .= chr $self->{next_char};
2329          ## Stay in the state          ## Stay in the state
# Line 1859  sub _get_next_token ($) { Line 2335  sub _get_next_token ($) {
2335              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2336              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2337            }->{$self->{next_char}}) {            }->{$self->{next_char}}) {
2338            !!!cp (215);
2339          ## Stay in the state          ## Stay in the state
2340          !!!next-input-character;          !!!next-input-character;
2341          redo A;          redo A;
2342        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2343            !!!cp (216);
2344          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2345          !!!next-input-character;          !!!next-input-character;
2346    
# Line 1870  sub _get_next_token ($) { Line 2348  sub _get_next_token ($) {
2348    
2349          redo A;          redo A;
2350        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2351            !!!cp (217);
2352          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2353    
2354          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 1880  sub _get_next_token ($) { Line 2359  sub _get_next_token ($) {
2359    
2360          redo A;          redo A;
2361        } else {        } else {
2362            !!!cp (218);
2363          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2364          #$self->{current_token}->{quirks} = 1;          #$self->{current_token}->{quirks} = 1;
2365    
# Line 1889  sub _get_next_token ($) { Line 2369  sub _get_next_token ($) {
2369        }        }
2370      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2371        if ($self->{next_char} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
2372            !!!cp (219);
2373          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2374          !!!next-input-character;          !!!next-input-character;
2375    
# Line 1896  sub _get_next_token ($) { Line 2377  sub _get_next_token ($) {
2377    
2378          redo A;          redo A;
2379        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
2380            !!!cp (220);
2381          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2382          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2383          ## reconsume          ## reconsume
# Line 1904  sub _get_next_token ($) { Line 2386  sub _get_next_token ($) {
2386    
2387          redo A;          redo A;
2388        } else {        } else {
2389            !!!cp (221);
2390          ## Stay in the state          ## Stay in the state
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
# Line 1919  sub _get_next_token ($) { Line 2402  sub _get_next_token ($) {
2402  sub _tokenize_attempt_to_consume_an_entity ($$$) {  sub _tokenize_attempt_to_consume_an_entity ($$$) {
2403    my ($self, $in_attr, $additional) = @_;    my ($self, $in_attr, $additional) = @_;
2404    
2405      my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
2406    
2407    if ({    if ({
2408         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
2409         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
2410         $additional => 1,         $additional => 1,
2411        }->{$self->{next_char}}) {        }->{$self->{next_char}}) {
2412        !!!cp (1001);
2413      ## Don't consume      ## Don't consume
2414      ## No error      ## No error
2415      return undef;      return undef;
# Line 1937  sub _tokenize_attempt_to_consume_an_enti Line 2423  sub _tokenize_attempt_to_consume_an_enti
2423          !!!next-input-character;          !!!next-input-character;
2424          if (0x0030 <= $self->{next_char} and          if (0x0030 <= $self->{next_char} and
2425              $self->{next_char} <= 0x0039) { # 0..9              $self->{next_char} <= 0x0039) { # 0..9
2426              !!!cp (1002);
2427            $code ||= 0;            $code ||= 0;
2428            $code *= 0x10;            $code *= 0x10;
2429            $code += $self->{next_char} - 0x0030;            $code += $self->{next_char} - 0x0030;
2430            redo X;            redo X;
2431          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{next_char} and
2432                   $self->{next_char} <= 0x0066) { # a..f                   $self->{next_char} <= 0x0066) { # a..f
2433              !!!cp (1003);
2434            $code ||= 0;            $code ||= 0;
2435            $code *= 0x10;            $code *= 0x10;
2436            $code += $self->{next_char} - 0x0060 + 9;            $code += $self->{next_char} - 0x0060 + 9;
2437            redo X;            redo X;
2438          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{next_char} and
2439                   $self->{next_char} <= 0x0046) { # A..F                   $self->{next_char} <= 0x0046) { # A..F
2440              !!!cp (1004);
2441            $code ||= 0;            $code ||= 0;
2442            $code *= 0x10;            $code *= 0x10;
2443            $code += $self->{next_char} - 0x0040 + 9;            $code += $self->{next_char} - 0x0040 + 9;
2444            redo X;            redo X;
2445          } elsif (not defined $code) { # no hexadecimal digit          } elsif (not defined $code) { # no hexadecimal digit
2446            !!!parse-error (type => 'bare hcro');            !!!cp (1005);
2447              !!!parse-error (type => 'bare hcro', line => $l, column => $c);
2448            !!!back-next-input-character ($x_char, $self->{next_char});            !!!back-next-input-character ($x_char, $self->{next_char});
2449            $self->{next_char} = 0x0023; # #            $self->{next_char} = 0x0023; # #
2450            return undef;            return undef;
2451          } elsif ($self->{next_char} == 0x003B) { # ;          } elsif ($self->{next_char} == 0x003B) { # ;
2452              !!!cp (1006);
2453            !!!next-input-character;            !!!next-input-character;
2454          } else {          } else {
2455            !!!parse-error (type => 'no refc');            !!!cp (1007);
2456              !!!parse-error (type => 'no refc', line => $l, column => $c);
2457          }          }
2458    
2459          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2460            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);            !!!cp (1008);
2461              !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2462            $code = 0xFFFD;            $code = 0xFFFD;
2463          } elsif ($code > 0x10FFFF) {          } elsif ($code > 0x10FFFF) {
2464            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);            !!!cp (1009);
2465              !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2466            $code = 0xFFFD;            $code = 0xFFFD;
2467          } elsif ($code == 0x000D) {          } elsif ($code == 0x000D) {
2468            !!!parse-error (type => 'CR character reference');            !!!cp (1010);
2469              !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2470            $code = 0x000A;            $code = 0x000A;
2471          } elsif (0x80 <= $code and $code <= 0x9F) {          } elsif (0x80 <= $code and $code <= 0x9F) {
2472            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);            !!!cp (1011);
2473              !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2474            $code = $c1_entity_char->{$code};            $code = $c1_entity_char->{$code};
2475          }          }
2476    
2477          return {type => CHARACTER_TOKEN, data => chr $code,          return {type => CHARACTER_TOKEN, data => chr $code,
2478                  has_reference => 1};                  has_reference => 1,
2479                    line => $l, column => $c,
2480                   };
2481        } # X        } # X
2482      } elsif (0x0030 <= $self->{next_char} and      } elsif (0x0030 <= $self->{next_char} and
2483               $self->{next_char} <= 0x0039) { # 0..9               $self->{next_char} <= 0x0039) { # 0..9
# Line 1988  sub _tokenize_attempt_to_consume_an_enti Line 2486  sub _tokenize_attempt_to_consume_an_enti
2486                
2487        while (0x0030 <= $self->{next_char} and        while (0x0030 <= $self->{next_char} and
2488                  $self->{next_char} <= 0x0039) { # 0..9                  $self->{next_char} <= 0x0039) { # 0..9
2489            !!!cp (1012);
2490          $code *= 10;          $code *= 10;
2491          $code += $self->{next_char} - 0x0030;          $code += $self->{next_char} - 0x0030;
2492                    
# Line 1995  sub _tokenize_attempt_to_consume_an_enti Line 2494  sub _tokenize_attempt_to_consume_an_enti
2494        }        }
2495    
2496        if ($self->{next_char} == 0x003B) { # ;        if ($self->{next_char} == 0x003B) { # ;
2497            !!!cp (1013);
2498          !!!next-input-character;          !!!next-input-character;
2499        } else {        } else {
2500          !!!parse-error (type => 'no refc');          !!!cp (1014);
2501            !!!parse-error (type => 'no refc', line => $l, column => $c);
2502        }        }
2503    
2504        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2505          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (1015);
2506            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2507          $code = 0xFFFD;          $code = 0xFFFD;
2508        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
2509          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1016);
2510            !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2511          $code = 0xFFFD;          $code = 0xFFFD;
2512        } elsif ($code == 0x000D) {        } elsif ($code == 0x000D) {
2513          !!!parse-error (type => 'CR character reference');          !!!cp (1017);
2514            !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2515          $code = 0x000A;          $code = 0x000A;
2516        } elsif (0x80 <= $code and $code <= 0x9F) {        } elsif (0x80 <= $code and $code <= 0x9F) {
2517          !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          !!!cp (1018);
2518            !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2519          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
2520        }        }
2521                
2522        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
2523                  line => $l, column => $c,
2524                 };
2525      } else {      } else {
2526        !!!parse-error (type => 'bare nero');        !!!cp (1019);
2527          !!!parse-error (type => 'bare nero', line => $l, column => $c);
2528        !!!back-next-input-character ($self->{next_char});        !!!back-next-input-character ($self->{next_char});
2529        $self->{next_char} = 0x0023; # #        $self->{next_char} = 0x0023; # #
2530        return undef;        return undef;
# Line 2045  sub _tokenize_attempt_to_consume_an_enti Line 2553  sub _tokenize_attempt_to_consume_an_enti
2553        $entity_name .= chr $self->{next_char};        $entity_name .= chr $self->{next_char};
2554        if (defined $EntityChar->{$entity_name}) {        if (defined $EntityChar->{$entity_name}) {
2555          if ($self->{next_char} == 0x003B) { # ;          if ($self->{next_char} == 0x003B) { # ;
2556              !!!cp (1020);
2557            $value = $EntityChar->{$entity_name};            $value = $EntityChar->{$entity_name};
2558            $match = 1;            $match = 1;
2559            !!!next-input-character;            !!!next-input-character;
2560            last;            last;
2561          } else {          } else {
2562              !!!cp (1021);
2563            $value = $EntityChar->{$entity_name};            $value = $EntityChar->{$entity_name};
2564            $match = -1;            $match = -1;
2565            !!!next-input-character;            !!!next-input-character;
2566          }          }
2567        } else {        } else {
2568            !!!cp (1022);
2569          $value .= chr $self->{next_char};          $value .= chr $self->{next_char};
2570          $match *= 2;          $match *= 2;
2571          !!!next-input-character;          !!!next-input-character;
# Line 2062  sub _tokenize_attempt_to_consume_an_enti Line 2573  sub _tokenize_attempt_to_consume_an_enti
2573      }      }
2574            
2575      if ($match > 0) {      if ($match > 0) {
2576        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        !!!cp (1023);
2577          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2578                  line => $l, column => $c,
2579                 };
2580      } elsif ($match < 0) {      } elsif ($match < 0) {
2581        !!!parse-error (type => 'no refc');        !!!parse-error (type => 'no refc', line => $l, column => $c);
2582        if ($in_attr and $match < -1) {        if ($in_attr and $match < -1) {
2583          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          !!!cp (1024);
2584        } else {          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
2585          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};                  line => $l, column => $c,
2586                   };
2587          } else {
2588            !!!cp (1025);
2589            return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2590                    line => $l, column => $c,
2591                   };
2592        }        }
2593      } else {      } else {
2594        !!!parse-error (type => 'bare ero');        !!!cp (1026);
2595          !!!parse-error (type => 'bare ero', line => $l, column => $c);
2596        ## NOTE: "No characters are consumed" in the spec.        ## NOTE: "No characters are consumed" in the spec.
2597        return {type => CHARACTER_TOKEN, data => '&'.$value};        return {type => CHARACTER_TOKEN, data => '&'.$value,
2598                  line => $l, column => $c,
2599                 };
2600      }      }
2601    } else {    } else {
2602        !!!cp (1027);
2603      ## no characters are consumed      ## no characters are consumed
2604      !!!parse-error (type => 'bare ero');      !!!parse-error (type => 'bare ero', line => $l, column => $c);
2605      return undef;      return undef;
2606    }    }
2607  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 2115  sub _construct_tree ($) { Line 2639  sub _construct_tree ($) {
2639        
2640    !!!next-token;    !!!next-token;
2641    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
2642    undef $self->{form_element};    undef $self->{form_element};
2643    undef $self->{head_element};    undef $self->{head_element};
2644    $self->{open_elements} = [];    $self->{open_elements} = [];
2645    undef $self->{inner_html_node};    undef $self->{inner_html_node};
2646    
2647      ## NOTE: The "initial" insertion mode.
2648    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
2649    
2650      ## NOTE: The "before html" insertion mode.
2651    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
2652      $self->{insertion_mode} = BEFORE_HEAD_IM;
2653    
2654      ## NOTE: The "before head" insertion mode and so on.
2655    $self->_tree_construction_main;    $self->_tree_construction_main;
2656  } # _construct_tree  } # _construct_tree
2657    
2658  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
2659    my $self = shift;    my $self = shift;
2660    
2661      ## NOTE: "initial" insertion mode
2662    
2663    INITIAL: {    INITIAL: {
2664      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
2665        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 2139  sub _tree_construction_initial ($) { Line 2671  sub _tree_construction_initial ($) {
2671        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
2672            defined $token->{public_identifier} or            defined $token->{public_identifier} or
2673            defined $token->{system_identifier}) {            defined $token->{system_identifier}) {
2674          !!!parse-error (type => 'not HTML5');          !!!cp ('t1');
2675            !!!parse-error (type => 'not HTML5', token => $token);
2676        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
2677            !!!cp ('t2');
2678          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)
2679          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
2680          } else {
2681            !!!cp ('t3');
2682        }        }
2683                
2684        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
2685          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
2686          ## NOTE: Default value for both |public_id| and |system_id| attributes
2687          ## are empty strings, so that we don't set any value in missing cases.
2688        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{public_identifier})
2689            if defined $token->{public_identifier};            if defined $token->{public_identifier};
2690        $doctype->system_id ($token->{system_identifier})        $doctype->system_id ($token->{system_identifier})
# Line 2156  sub _tree_construction_initial ($) { Line 2694  sub _tree_construction_initial ($) {
2694        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
2695                
2696        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
2697            !!!cp ('t4');
2698          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
2699        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{public_identifier}) {
2700          my $pubid = $token->{public_identifier};          my $pubid = $token->{public_identifier};
# Line 2234  sub _tree_construction_initial ($) { Line 2773  sub _tree_construction_initial ($) {
2773            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,
2774            "HTML" => 1,            "HTML" => 1,
2775          }->{$pubid}) {          }->{$pubid}) {
2776              !!!cp ('t5');
2777            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
2778          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or
2779                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {
2780            if (defined $token->{system_identifier}) {            if (defined $token->{system_identifier}) {
2781                !!!cp ('t6');
2782              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
2783            } else {            } else {
2784                !!!cp ('t7');
2785              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
2786            }            }
2787          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or
2788                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {
2789              !!!cp ('t8');
2790            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
2791            } else {
2792              !!!cp ('t9');
2793          }          }
2794          } else {
2795            !!!cp ('t10');
2796        }        }
2797        if (defined $token->{system_identifier}) {        if (defined $token->{system_identifier}) {
2798          my $sysid = $token->{system_identifier};          my $sysid = $token->{system_identifier};
2799          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
2800          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
2801              ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"
2802            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
2803              !!!cp ('t11');
2804            } else {
2805              !!!cp ('t12');
2806          }          }
2807          } else {
2808            !!!cp ('t13');
2809        }        }
2810                
2811        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
2812        !!!next-token;        !!!next-token;
2813        return;        return;
2814      } elsif ({      } elsif ({
# Line 2263  sub _tree_construction_initial ($) { Line 2816  sub _tree_construction_initial ($) {
2816                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
2817                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
2818               }->{$token->{type}}) {               }->{$token->{type}}) {
2819        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
2820          !!!parse-error (type => 'no DOCTYPE', token => $token);
2821        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2822        ## Go to the root element phase        ## Go to the "before html" insertion mode.
2823        ## reprocess        ## reprocess
2824        return;        return;
2825      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
# Line 2273  sub _tree_construction_initial ($) { Line 2827  sub _tree_construction_initial ($) {
2827          ## Ignore the token          ## Ignore the token
2828    
2829          unless (length $token->{data}) {          unless (length $token->{data}) {
2830            ## Stay in the phase            !!!cp ('t15');
2831              ## Stay in the insertion mode.
2832            !!!next-token;            !!!next-token;
2833            redo INITIAL;            redo INITIAL;
2834            } else {
2835              !!!cp ('t16');
2836          }          }
2837          } else {
2838            !!!cp ('t17');
2839        }        }
2840    
2841        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
2842        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2843        ## Go to the root element phase        ## Go to the "before html" insertion mode.
2844        ## reprocess        ## reprocess
2845        return;        return;
2846      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
2847          !!!cp ('t18');
2848        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
2849        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
2850                
2851        ## Stay in the phase.        ## Stay in the insertion mode.
2852        !!!next-token;        !!!next-token;
2853        redo INITIAL;        redo INITIAL;
2854      } else {      } else {
2855        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
2856      }      }
2857    } # INITIAL    } # INITIAL
2858    
2859      die "$0: _tree_construction_initial: This should be never reached";
2860  } # _tree_construction_initial  } # _tree_construction_initial
2861    
2862  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
2863    my $self = shift;    my $self = shift;
2864    
2865      ## NOTE: "before html" insertion mode.
2866        
2867    B: {    B: {
2868        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
2869          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
2870            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
2871          ## Ignore the token          ## Ignore the token
2872          ## Stay in the phase          ## Stay in the insertion mode.
2873          !!!next-token;          !!!next-token;
2874          redo B;          redo B;
2875        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
2876            !!!cp ('t20');
2877          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
2878          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
2879          ## Stay in the phase          ## Stay in the insertion mode.
2880          !!!next-token;          !!!next-token;
2881          redo B;          redo B;
2882        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
# Line 2318  sub _tree_construction_root_element ($) Line 2884  sub _tree_construction_root_element ($)
2884            ## Ignore the token.            ## Ignore the token.
2885    
2886            unless (length $token->{data}) {            unless (length $token->{data}) {
2887              ## Stay in the phase              !!!cp ('t21');
2888                ## Stay in the insertion mode.
2889              !!!next-token;              !!!next-token;
2890              redo B;              redo B;
2891              } else {
2892                !!!cp ('t22');
2893            }            }
2894            } else {
2895              !!!cp ('t23');
2896          }          }
2897    
2898          $self->{application_cache_selection}->(undef);          $self->{application_cache_selection}->(undef);
2899    
2900          #          #
2901        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
2902          if ($token->{tag_name} eq 'html' and          if ($token->{tag_name} eq 'html') {
2903              $token->{attributes}->{manifest}) {            my $root_element;
2904            $self->{application_cache_selection}            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}, $token);
2905                 ->($token->{attributes}->{manifest}->{value});            $self->{document}->append_child ($root_element);
2906            ## ISSUE: No relative reference resolution?            push @{$self->{open_elements}},
2907                  [$root_element, $el_category->{html}];
2908    
2909              if ($token->{attributes}->{manifest}) {
2910                !!!cp ('t24');
2911                $self->{application_cache_selection}
2912                    ->($token->{attributes}->{manifest}->{value});
2913                ## ISSUE: Spec is unclear on relative references.
2914                ## According to Hixie (#whatwg 2008-03-19), it should be
2915                ## resolved against the base URI of the document in HTML
2916                ## or xml:base of the element in XHTML.
2917              } else {
2918                !!!cp ('t25');
2919                $self->{application_cache_selection}->(undef);
2920              }
2921    
2922              !!!next-token;
2923              return; ## Go to the "before head" insertion mode.
2924          } else {          } else {
2925            $self->{application_cache_selection}->(undef);            !!!cp ('t25.1');
2926              #
2927          }          }
   
         ## ISSUE: There is an issue in the spec  
         #  
2928        } elsif ({        } elsif ({
2929                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
2930                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
2931                 }->{$token->{type}}) {                 }->{$token->{type}}) {
2932          $self->{application_cache_selection}->(undef);          !!!cp ('t26');
   
         ## ISSUE: There is an issue in the spec  
2933          #          #
2934        } else {        } else {
2935          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2936        }        }
2937    
2938        my $root_element; !!!create-element ($root_element, 'html');      my $root_element; !!!create-element ($root_element, 'html',, $token);
2939        $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
2940        push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
2941        ## reprocess  
2942        #redo B;      $self->{application_cache_selection}->(undef);
2943        return; ## Go to the main phase.  
2944        ## NOTE: Reprocess the token.
2945        return; ## Go to the "before head" insertion mode.
2946    
2947        ## ISSUE: There is an issue in the spec
2948    } # B    } # B
2949    
2950      die "$0: _tree_construction_root_element: This should never be reached";
2951  } # _tree_construction_root_element  } # _tree_construction_root_element
2952    
2953  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2372  sub _reset_insertion_mode ($) { Line 2962  sub _reset_insertion_mode ($) {
2962            
2963      ## Step 3      ## Step 3
2964      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
2965        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
2966          $last = 1;          $last = 1;
2967          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
2968            if ($self->{inner_html_node}->[1] eq 'td' or            if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) {
2969                $self->{inner_html_node}->[1] eq 'th') {              !!!cp ('t27');
2970              #              #
2971            } else {            } else {
2972                !!!cp ('t28');
2973              $node = $self->{inner_html_node};              $node = $self->{inner_html_node};
2974            }            }
2975          }          }
# Line 2392  sub _reset_insertion_mode ($) { Line 2978  sub _reset_insertion_mode ($) {
2978        ## Step 4..13        ## Step 4..13
2979        my $new_mode = {        my $new_mode = {
2980                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
2981                          ## NOTE: |option| and |optgroup| do not set
2982                          ## insertion mode to "in select" by themselves.
2983                        td => IN_CELL_IM,                        td => IN_CELL_IM,
2984                        th => IN_CELL_IM,                        th => IN_CELL_IM,
2985                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
# Line 2404  sub _reset_insertion_mode ($) { Line 2992  sub _reset_insertion_mode ($) {
2992                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
2993                        body => IN_BODY_IM,                        body => IN_BODY_IM,
2994                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
2995                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
2996                         ## TODO: Foreign namespace case OK?
2997        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
2998                
2999        ## Step 14        ## Step 14
3000        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3001          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3002              !!!cp ('t29');
3003            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3004          } else {          } else {
3005              ## ISSUE: Can this state be reached?
3006              !!!cp ('t30');
3007            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3008          }          }
3009          return;          return;
3010          } else {
3011            !!!cp ('t31');
3012        }        }
3013                
3014        ## Step 15        ## Step 15
# Line 2427  sub _reset_insertion_mode ($) { Line 3021  sub _reset_insertion_mode ($) {
3021        ## Step 17        ## Step 17
3022        redo S3;        redo S3;
3023      } # S3      } # S3
3024    
3025      die "$0: _reset_insertion_mode: This line should never be reached";
3026  } # _reset_insertion_mode  } # _reset_insertion_mode
3027    
3028  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2448  sub _tree_construction_main ($) { Line 3044  sub _tree_construction_main ($) {
3044      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3045      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3046        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3047            !!!cp ('t32');
3048          return;          return;
3049        }        }
3050      }      }
# Line 2462  sub _tree_construction_main ($) { Line 3059  sub _tree_construction_main ($) {
3059    
3060        ## Step 6        ## Step 6
3061        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3062            !!!cp ('t33_1');
3063          #          #
3064        } else {        } else {
3065          my $in_open_elements;          my $in_open_elements;
3066          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3067            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3068                !!!cp ('t33');
3069              $in_open_elements = 1;              $in_open_elements = 1;
3070              last OE;              last OE;
3071            }            }
3072          }          }
3073          if ($in_open_elements) {          if ($in_open_elements) {
3074              !!!cp ('t34');
3075            #            #
3076          } else {          } else {
3077              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3078              !!!cp ('t35');
3079            redo S4;            redo S4;
3080          }          }
3081        }        }
# Line 2496  sub _tree_construction_main ($) { Line 3098  sub _tree_construction_main ($) {
3098    
3099        ## Step 11        ## Step 11
3100        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3101            !!!cp ('t36');
3102          ## Step 7'          ## Step 7'
3103          $i++;          $i++;
3104          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3105                    
3106          redo S7;          redo S7;
3107        }        }
3108    
3109          !!!cp ('t37');
3110      } # S7      } # S7
3111    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3112    
3113    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3114      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3115        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3116            !!!cp ('t38');
3117          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3118          return;          return;
3119        }        }
3120      }      }
3121    
3122        !!!cp ('t39');
3123    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3124    
3125    my $parse_rcdata = sub ($$) {    my $insert;
3126      my ($content_model_flag, $insert) = @_;  
3127      my $parse_rcdata = sub ($) {
3128        my ($content_model_flag) = @_;
3129    
3130      ## Step 1      ## Step 1
3131      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3132      my $el;      my $el;
3133      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $start_tag_name, $token->{attributes}, $token);
3134    
3135      ## Step 2      ## Step 2
3136      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3137    
3138      ## Step 3      ## Step 3
3139      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2533  sub _tree_construction_main ($) { Line 3143  sub _tree_construction_main ($) {
3143      my $text = '';      my $text = '';
3144      !!!next-token;      !!!next-token;
3145      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3146          !!!cp ('t40');
3147        $text .= $token->{data};        $text .= $token->{data};
3148        !!!next-token;        !!!next-token;
3149      }      }
3150    
3151      ## Step 5      ## Step 5
3152      if (length $text) {      if (length $text) {
3153          !!!cp ('t41');
3154        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
3155        $el->append_child ($text);        $el->append_child ($text);
3156      }      }
# Line 2547  sub _tree_construction_main ($) { Line 3159  sub _tree_construction_main ($) {
3159      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
3160    
3161      ## Step 7      ## Step 7
3162      if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
3163            $token->{tag_name} eq $start_tag_name) {
3164          !!!cp ('t42');
3165        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
3166      } else {      } else {
3167        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
3168          if ($content_model_flag == CDATA_CONTENT_MODEL) {
3169            !!!cp ('t43');
3170            !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3171          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
3172            !!!cp ('t44');
3173            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
3174          } else {
3175            die "$0: $content_model_flag in parse_rcdata";
3176          }
3177      }      }
3178      !!!next-token;      !!!next-token;
3179    }; # $parse_rcdata    }; # $parse_rcdata
3180    
3181    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
3182      my $script_el;      my $script_el;
3183      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, 'script', $token->{attributes}, $token);
3184      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
3185    
3186      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
# Line 2571  sub _tree_construction_main ($) { Line 3189  sub _tree_construction_main ($) {
3189      my $text = '';      my $text = '';
3190      !!!next-token;      !!!next-token;
3191      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
3192          !!!cp ('t45');
3193        $text .= $token->{data};        $text .= $token->{data};
3194        !!!next-token;        !!!next-token;
3195      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
3196      if (length $text) {      if (length $text) {
3197          !!!cp ('t46');
3198        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
3199      }      }
3200                                
# Line 2582  sub _tree_construction_main ($) { Line 3202  sub _tree_construction_main ($) {
3202    
3203      if ($token->{type} == END_TAG_TOKEN and      if ($token->{type} == END_TAG_TOKEN and
3204          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
3205          !!!cp ('t47');
3206        ## Ignore the token        ## Ignore the token
3207      } else {      } else {
3208        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
3209          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3210        ## ISSUE: And ignore?        ## ISSUE: And ignore?
3211        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3212      }      }
3213            
3214      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
3215          !!!cp ('t49');
3216        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3217      } else {      } else {
3218          !!!cp ('t50');
3219        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
3220        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
3221    
# Line 2605  sub _tree_construction_main ($) { Line 3229  sub _tree_construction_main ($) {
3229      !!!next-token;      !!!next-token;
3230    }; # $script_start_tag    }; # $script_start_tag
3231    
3232      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
3233      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
3234      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
3235    
3236    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
3237      my $tag_name = shift;      my $end_tag_token = shift;
3238        my $tag_name = $end_tag_token->{tag_name};
3239    
3240        ## NOTE: The adoption agency algorithm (AAA).
3241    
3242      FET: {      FET: {
3243        ## Step 1        ## Step 1
3244        my $formatting_element;        my $formatting_element;
3245        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
3246        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3247          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3248              !!!cp ('t52');
3249              last AFE;
3250            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
3251                         eq $tag_name) {
3252              !!!cp ('t51');
3253            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
3254            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
3255            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
3256          }          }
3257        } # AFE        } # AFE
3258        unless (defined $formatting_element) {        unless (defined $formatting_element) {
3259          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
3260            !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);
3261          ## Ignore the token          ## Ignore the token
3262          !!!next-token;          !!!next-token;
3263          return;          return;
# Line 2634  sub _tree_construction_main ($) { Line 3269  sub _tree_construction_main ($) {
3269          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3270          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
3271            if ($in_scope) {            if ($in_scope) {
3272                !!!cp ('t54');
3273              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
3274              last INSCOPE;              last INSCOPE;
3275            } else { # in open elements but not in scope            } else { # in open elements but not in scope
3276              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
3277                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3278                                token => $end_tag_token);
3279              ## Ignore the token              ## Ignore the token
3280              !!!next-token;              !!!next-token;
3281              return;              return;
3282            }            }
3283          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
3284                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
3285            $in_scope = 0;            $in_scope = 0;
3286          }          }
3287        } # INSCOPE        } # INSCOPE
3288        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
3289          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
3290            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3291                            token => $end_tag_token);
3292          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
3293          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
3294          return;          return;
3295        }        }
3296        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
3297          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
3298            !!!parse-error (type => 'not closed',
3299                            value => $self->{open_elements}->[-1]->[0]
3300                                ->manakai_local_name,
3301                            token => $end_tag_token);
3302        }        }
3303                
3304        ## Step 2        ## Step 2
# Line 2664  sub _tree_construction_main ($) { Line 3306  sub _tree_construction_main ($) {
3306        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
3307        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3308          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3309          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
3310              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
3311              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
3312               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
3313              !!!cp ('t59');
3314            $furthest_block = $node;            $furthest_block = $node;
3315            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
3316          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
3317              !!!cp ('t60');
3318            last OE;            last OE;
3319          }          }
3320        } # OE        } # OE
3321                
3322        ## Step 3        ## Step 3
3323        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
3324            !!!cp ('t61');
3325          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
3326          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
3327          !!!next-token;          !!!next-token;
# Line 2689  sub _tree_construction_main ($) { Line 3334  sub _tree_construction_main ($) {
3334        ## Step 5        ## Step 5
3335        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
3336        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
3337            !!!cp ('t62');
3338          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
3339        }        }
3340                
# Line 2711  sub _tree_construction_main ($) { Line 3357  sub _tree_construction_main ($) {
3357          S7S2: {          S7S2: {
3358            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
3359              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
3360                  !!!cp ('t63');
3361                $node_i_in_active = $_;                $node_i_in_active = $_;
3362                last S7S2;                last S7S2;
3363              }              }
# Line 2724  sub _tree_construction_main ($) { Line 3371  sub _tree_construction_main ($) {
3371                    
3372          ## Step 4          ## Step 4
3373          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
3374              !!!cp ('t64');
3375            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
3376          }          }
3377                    
3378          ## Step 5          ## Step 5
3379          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
3380              !!!cp ('t65');
3381            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
3382            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
3383            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2746  sub _tree_construction_main ($) { Line 3395  sub _tree_construction_main ($) {
3395        } # S7          } # S7  
3396                
3397        ## Step 8        ## Step 8
3398        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
3399            my $foster_parent_element;
3400            my $next_sibling;
3401            OE: for (reverse 0..$#{$self->{open_elements}}) {
3402              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
3403                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3404                                 if (defined $parent and $parent->node_type == 1) {
3405                                   !!!cp ('t65.1');
3406                                   $foster_parent_element = $parent;
3407                                   $next_sibling = $self->{open_elements}->[$_]->[0];
3408                                 } else {
3409                                   !!!cp ('t65.2');
3410                                   $foster_parent_element
3411                                     = $self->{open_elements}->[$_ - 1]->[0];
3412                                 }
3413                                 last OE;
3414                               }
3415                             } # OE
3416                             $foster_parent_element = $self->{open_elements}->[0]->[0]
3417                               unless defined $foster_parent_element;
3418            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
3419            $open_tables->[-1]->[1] = 1; # tainted
3420          } else {
3421            !!!cp ('t65.3');
3422            $common_ancestor_node->[0]->append_child ($last_node->[0]);
3423          }
3424                
3425        ## Step 9        ## Step 9
3426        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2763  sub _tree_construction_main ($) { Line 3437  sub _tree_construction_main ($) {
3437        my $i;        my $i;
3438        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3439          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
3440              !!!cp ('t66');
3441            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
3442            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
3443          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
3444              !!!cp ('t67');
3445            $i = $_;            $i = $_;
3446          }          }
3447        } # AFE        } # AFE
# Line 2775  sub _tree_construction_main ($) { Line 3451  sub _tree_construction_main ($) {
3451        undef $i;        undef $i;
3452        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3453          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
3454              !!!cp ('t68');
3455            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
3456            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
3457          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
3458              !!!cp ('t69');
3459            $i = $_;            $i = $_;
3460          }          }
3461        } # OE        } # OE
# Line 2788  sub _tree_construction_main ($) { Line 3466  sub _tree_construction_main ($) {
3466      } # FET      } # FET
3467    }; # $formatting_end_tag    }; # $formatting_end_tag
3468    
3469    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
3470      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
3471    }; # $insert_to_current    }; # $insert_to_current
3472    
3473    my $insert_to_foster = sub {    my $insert_to_foster = sub {
3474                         my $child = shift;      my $child = shift;
3475                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3476                              table => 1, tbody => 1, tfoot => 1,        # MUST
3477                              thead => 1, tr => 1,        my $foster_parent_element;
3478                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
3479                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
3480                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
3481                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3482                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
3483                                   !!!cp ('t70');
3484                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
3485                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
3486                               } else {                               } else {
3487                                   !!!cp ('t71');
3488                                 $foster_parent_element                                 $foster_parent_element
3489                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
3490                               }                               }
# Line 2818  sub _tree_construction_main ($) { Line 3495  sub _tree_construction_main ($) {
3495                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
3496                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
3497                             ($child, $next_sibling);                             ($child, $next_sibling);
3498                         } else {        $open_tables->[-1]->[1] = 1; # tainted
3499                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
3500                         }        !!!cp ('t72');
3501          $self->{open_elements}->[-1]->[0]->append_child ($child);
3502        }
3503    }; # $insert_to_foster    }; # $insert_to_foster
3504    
   my $insert;  
   
3505    B: {    B: {
3506      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3507        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
3508          !!!parse-error (type => 'DOCTYPE in the middle', token => $token);
3509        ## Ignore the token        ## Ignore the token
3510        ## Stay in the phase        ## Stay in the phase
3511        !!!next-token;        !!!next-token;
3512        redo B;        redo B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         #  
       } else {  
         ## 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_TOKEN, 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]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
3513      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
3514               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
3515        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
3516          ## Turn into the main phase          !!!cp ('t79');
3517          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html', token => $token);
3518          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
3519        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
3520          ## Turn into the main phase          !!!cp ('t80');
3521          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html', token => $token);
3522          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
3523          } else {
3524            !!!cp ('t81');
3525        }        }
3526    
3527  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
3528  ## ISSUE: "<html>" in fragment is not a parse error.        !!!parse-error (type => 'not first start tag', token => $token);
       unless ($token->{first_start_tag}) {  
         !!!parse-error (type => 'not first start tag');  
       }  
3529        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
3530        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
3531          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
3532              !!!cp ('t84');
3533            $top_el->set_attribute_ns            $top_el->set_attribute_ns
3534              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
3535               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
# Line 2890  sub _tree_construction_main ($) { Line 3540  sub _tree_construction_main ($) {
3540      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3541        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3542        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
3543            !!!cp ('t85');
3544          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3545        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
3546            !!!cp ('t86');
3547          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
3548        } else {        } else {
3549            !!!cp ('t87');
3550          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
3551        }        }
3552        !!!next-token;        !!!next-token;
# Line 2901  sub _tree_construction_main ($) { Line 3554  sub _tree_construction_main ($) {
3554      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & HEAD_IMS) {
3555        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
3556          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3557            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3558                !!!cp ('t88.2');
3559                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3560              } else {
3561                !!!cp ('t88.1');
3562                ## Ignore the token.
3563                !!!next-token;
3564                redo B;
3565              }
3566            unless (length $token->{data}) {            unless (length $token->{data}) {
3567                !!!cp ('t88');
3568              !!!next-token;              !!!next-token;
3569              redo B;              redo B;
3570            }            }
3571          }          }
3572    
3573          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3574              !!!cp ('t89');
3575            ## As if <head>            ## As if <head>
3576            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, 'head',, $token);
3577            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3578            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
3579                  [$self->{head_element}, $el_category->{head}];
3580    
3581            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
3582            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
3583    
3584            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
3585          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3586              !!!cp ('t90');
3587            ## As if </noscript>            ## As if </noscript>
3588            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
3589            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#character', token => $token);
3590                        
3591            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
3592            ## As if </head>            ## As if </head>
# Line 2929  sub _tree_construction_main ($) { Line 3594  sub _tree_construction_main ($) {
3594    
3595            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
3596          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3597              !!!cp ('t91');
3598            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
3599    
3600            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
3601            } else {
3602              !!!cp ('t92');
3603          }          }
3604    
3605              ## "after head" insertion mode          ## "after head" insertion mode
3606              ## As if <body>          ## As if <body>
3607              !!!insert-element ('body');          !!!insert-element ('body',, $token);
3608              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
3609              ## reprocess          ## reprocess
3610            redo B;
3611          } elsif ($token->{type} == START_TAG_TOKEN) {
3612            if ($token->{tag_name} eq 'head') {
3613              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3614                !!!cp ('t93');
3615                !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);
3616                $self->{open_elements}->[-1]->[0]->append_child
3617                    ($self->{head_element});
3618                push @{$self->{open_elements}},
3619                    [$self->{head_element}, $el_category->{head}];
3620                $self->{insertion_mode} = IN_HEAD_IM;
3621                !!!next-token;
3622              redo B;              redo B;
           } elsif ($token->{type} == START_TAG_TOKEN) {  
             if ($token->{tag_name} eq 'head') {  
               if ($self->{insertion_mode} == BEFORE_HEAD_IM) {  
                 !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});  
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];  
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 !!!next-token;  
                 redo B;  
3623                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3624                    !!!cp ('t94');
3625                  #                  #
3626                } else {                } else {
3627                  !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!cp ('t95');
3628                    !!!parse-error (type => 'in head:head', token => $token); # or in head noscript
3629                  ## Ignore the token                  ## Ignore the token
3630                  !!!next-token;                  !!!next-token;
3631                  redo B;                  redo B;
3632                }                }
3633              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3634                  !!!cp ('t96');
3635                ## As if <head>                ## As if <head>
3636                !!!create-element ($self->{head_element}, 'head');                !!!create-element ($self->{head_element}, 'head',, $token);
3637                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3638                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                push @{$self->{open_elements}},
3639                      [$self->{head_element}, $el_category->{head}];
3640    
3641                $self->{insertion_mode} = IN_HEAD_IM;                $self->{insertion_mode} = IN_HEAD_IM;
3642                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
3643                } else {
3644                  !!!cp ('t97');
3645              }              }
3646    
3647              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
3648                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3649                    !!!cp ('t98');
3650                  ## As if </noscript>                  ## As if </noscript>
3651                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3652                  !!!parse-error (type => 'in noscript:base');                  !!!parse-error (type => 'in noscript:base', token => $token);
3653                                
3654                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3655                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3656                  } else {
3657                    !!!cp ('t99');
3658                }                }
3659    
3660                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3661                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3662                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t100');
3663                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3664                    push @{$self->{open_elements}},
3665                        [$self->{head_element}, $el_category->{head}];
3666                  } else {
3667                    !!!cp ('t101');
3668                }                }
3669                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3670                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3671                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3672                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3673                !!!next-token;                !!!next-token;
3674                redo B;                redo B;
3675              } elsif ($token->{tag_name} eq 'link') {              } elsif ($token->{tag_name} eq 'link') {
3676                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3677                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3678                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t102');
3679                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3680                    push @{$self->{open_elements}},
3681                        [$self->{head_element}, $el_category->{head}];
3682                  } else {
3683                    !!!cp ('t103');
3684                }                }
3685                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3686                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3687                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3688                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3689                !!!next-token;                !!!next-token;
3690                redo B;                redo B;
3691              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
3692                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3693                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3694                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t104');
3695                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3696                    push @{$self->{open_elements}},
3697                        [$self->{head_element}, $el_category->{head}];
3698                  } else {
3699                    !!!cp ('t105');
3700                }                }
3701                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3702                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3703    
3704                unless ($self->{confident}) {                unless ($self->{confident}) {
3705                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) { ## TODO: And if supported
3706                      !!!cp ('t106');
3707                    $self->{change_encoding}                    $self->{change_encoding}
3708                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
3709                             $token);
3710                                        
3711                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3712                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
# Line 3025  sub _tree_construction_main ($) { Line 3719  sub _tree_construction_main ($) {
3719                            [\x09-\x0D\x20]*=                            [\x09-\x0D\x20]*=
3720                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
3721                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
3722                        !!!cp ('t107');
3723                      $self->{change_encoding}                      $self->{change_encoding}
3724                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
3725                               $token);
3726                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3727                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
3728                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
3729                                                     ->{has_reference});                                                     ->{has_reference});
3730                      } else {
3731                        !!!cp ('t108');
3732                    }                    }
3733                  }                  }
3734                } else {                } else {
3735                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
3736                      !!!cp ('t109');
3737                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3738                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
3739                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
3740                                                 ->{has_reference});                                                 ->{has_reference});
3741                  }                  }
3742                  if ($token->{attributes}->{content}) {                  if ($token->{attributes}->{content}) {
3743                      !!!cp ('t110');
3744                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3745                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
3746                                             $token->{attributes}->{content}                                             $token->{attributes}->{content}
# Line 3048  sub _tree_construction_main ($) { Line 3748  sub _tree_construction_main ($) {
3748                  }                  }
3749                }                }
3750    
3751                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3752                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3753                !!!next-token;                !!!next-token;
3754                redo B;                redo B;
3755              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
3756                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3757                    !!!cp ('t111');
3758                  ## As if </noscript>                  ## As if </noscript>
3759                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3760                  !!!parse-error (type => 'in noscript:title');                  !!!parse-error (type => 'in noscript:title', token => $token);
3761                                
3762                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3763                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3764                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3765                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t112');
3766                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3767                    push @{$self->{open_elements}},
3768                        [$self->{head_element}, $el_category->{head}];
3769                  } else {
3770                    !!!cp ('t113');
3771                }                }
3772    
3773                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3774                my $parent = defined $self->{head_element} ? $self->{head_element}                my $parent = defined $self->{head_element} ? $self->{head_element}
3775                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
3776                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL);
3777                                sub { $parent->append_child ($_[0]) });                pop @{$self->{open_elements}} # <head>
               pop @{$self->{open_elements}}  
3778                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3779                redo B;                redo B;
3780              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style') {
# Line 3078  sub _tree_construction_main ($) { Line 3782  sub _tree_construction_main ($) {
3782                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
3783                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3784                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3785                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
3786                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3787                    push @{$self->{open_elements}},
3788                        [$self->{head_element}, $el_category->{head}];
3789                  } else {
3790                    !!!cp ('t115');
3791                }                }
3792                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
3793                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3794                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3795                redo B;                redo B;
3796              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
3797                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
3798                    !!!cp ('t116');
3799                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
3800                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3801                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
3802                  !!!next-token;                  !!!next-token;
3803                  redo B;                  redo B;
3804                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3805                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
3806                    !!!parse-error (type => 'in noscript:noscript', token => $token);
3807                  ## Ignore the token                  ## Ignore the token
3808                  !!!next-token;                  !!!next-token;
3809                  redo B;                  redo B;
3810                } else {                } else {
3811                    !!!cp ('t118');
3812                  #                  #
3813                }                }
3814              } elsif ($token->{tag_name} eq 'script') {              } elsif ($token->{tag_name} eq 'script') {
3815                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3816                    !!!cp ('t119');
3817                  ## As if </noscript>                  ## As if </noscript>
3818                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3819                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:script', token => $token);
3820                                
3821                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3822                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3823                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3824                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t120');
3825                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3826                    push @{$self->{open_elements}},
3827                        [$self->{head_element}, $el_category->{head}];
3828                  } else {
3829                    !!!cp ('t121');
3830                }                }
3831    
3832                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3833                $script_start_tag->($insert_to_current);                $script_start_tag->();
3834                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3835                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3836                redo B;                redo B;
3837              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
3838                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
3839                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3840                    !!!cp ('t122');
3841                  ## As if </noscript>                  ## As if </noscript>
3842                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3843                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);
3844                                    
3845                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3846                  ## As if </head>                  ## As if </head>
# Line 3131  sub _tree_construction_main ($) { Line 3848  sub _tree_construction_main ($) {
3848                                    
3849                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
3850                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3851                    !!!cp ('t124');
3852                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3853                                    
3854                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
3855                  } else {
3856                    !!!cp ('t125');
3857                }                }
3858    
3859                ## "after head" insertion mode                ## "after head" insertion mode
3860                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3861                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
3862                    !!!cp ('t126');
3863                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
3864                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
3865                    !!!cp ('t127');
3866                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
3867                } else {                } else {
3868                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
# Line 3148  sub _tree_construction_main ($) { Line 3870  sub _tree_construction_main ($) {
3870                !!!next-token;                !!!next-token;
3871                redo B;                redo B;
3872              } else {              } else {
3873                  !!!cp ('t128');
3874                #                #
3875              }              }
3876    
3877              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3878                  !!!cp ('t129');
3879                ## As if </noscript>                ## As if </noscript>
3880                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3881                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
3882                                
3883                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
3884                ## As if </head>                ## As if </head>
# Line 3162  sub _tree_construction_main ($) { Line 3886  sub _tree_construction_main ($) {
3886    
3887                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
3888              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3889                  !!!cp ('t130');
3890                ## As if </head>                ## As if </head>
3891                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3892    
3893                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
3894                } else {
3895                  !!!cp ('t131');
3896              }              }
3897    
3898              ## "after head" insertion mode              ## "after head" insertion mode
3899              ## As if <body>              ## As if <body>
3900              !!!insert-element ('body');              !!!insert-element ('body',, $token);
3901              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
3902              ## reprocess              ## reprocess
3903              redo B;              redo B;
3904            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3905              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
3906                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3907                    !!!cp ('t132');
3908                  ## As if <head>                  ## As if <head>
3909                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head',, $token);
3910                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3911                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3912                        [$self->{head_element}, $el_category->{head}];
3913    
3914                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3915                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 3188  sub _tree_construction_main ($) { Line 3917  sub _tree_construction_main ($) {
3917                  !!!next-token;                  !!!next-token;
3918                  redo B;                  redo B;
3919                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3920                    !!!cp ('t133');
3921                  ## As if </noscript>                  ## As if </noscript>
3922                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3923                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/head', token => $token);
3924                                    
3925                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3926                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 3198  sub _tree_construction_main ($) { Line 3928  sub _tree_construction_main ($) {
3928                  !!!next-token;                  !!!next-token;
3929                  redo B;                  redo B;
3930                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3931                    !!!cp ('t134');
3932                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3933                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
3934                  !!!next-token;                  !!!next-token;
3935                  redo B;                  redo B;
3936                } else {                } else {
3937                    !!!cp ('t135');
3938                  #                  #
3939                }                }
3940              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
3941                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3942                    !!!cp ('t136');
3943                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3944                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3945                  !!!next-token;                  !!!next-token;
3946                  redo B;                  redo B;
3947                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3948                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!cp ('t137');
3949                    !!!parse-error (type => 'unmatched end tag:noscript', token => $token);
3950                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
3951                  !!!next-token;                  !!!next-token;
3952                  redo B;                  redo B;
3953                } else {                } else {
3954                    !!!cp ('t138');
3955                  #                  #
3956                }                }
3957              } elsif ({              } elsif ({
3958                        body => 1, html => 1,                        body => 1, html => 1,
3959                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3960                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3961                    !!!cp ('t139');
3962                  ## As if <head>                  ## As if <head>
3963                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head',, $token);
3964                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3965                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3966                        [$self->{head_element}, $el_category->{head}];
3967    
3968                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3969                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3970                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3971                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t140');
3972                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
3973                  ## Ignore the token                  ## Ignore the token
3974                  !!!next-token;                  !!!next-token;
3975                  redo B;                  redo B;
3976                  } else {
3977                    !!!cp ('t141');
3978                }                }
3979                                
3980                #                #
# Line 3242  sub _tree_construction_main ($) { Line 3982  sub _tree_construction_main ($) {
3982                        p => 1, br => 1,                        p => 1, br => 1,
3983                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3984                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3985                    !!!cp ('t142');
3986                  ## As if <head>                  ## As if <head>
3987                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head',, $token);
3988                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3989                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3990                        [$self->{head_element}, $el_category->{head}];
3991    
3992                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3993                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3994                  } else {
3995                    !!!cp ('t143');
3996                }                }
3997    
3998                #                #
3999              } else {              } else {
4000                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4001                    !!!cp ('t144');
4002                  #                  #
4003                } else {                } else {
4004                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t145');
4005                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4006                  ## Ignore the token                  ## Ignore the token
4007                  !!!next-token;                  !!!next-token;
4008                  redo B;                  redo B;
# Line 3264  sub _tree_construction_main ($) { Line 4010  sub _tree_construction_main ($) {
4010              }              }
4011    
4012              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4013                  !!!cp ('t146');
4014                ## As if </noscript>                ## As if </noscript>
4015                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4016                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
4017                                
4018                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4019                ## As if </head>                ## As if </head>
# Line 3274  sub _tree_construction_main ($) { Line 4021  sub _tree_construction_main ($) {
4021    
4022                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4023              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4024                  !!!cp ('t147');
4025                ## As if </head>                ## As if </head>
4026                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4027    
4028                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4029              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4030                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
4031                  !!!cp ('t148');
4032                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4033                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
4034                !!!next-token;                !!!next-token;
4035                redo B;                redo B;
4036                } else {
4037                  !!!cp ('t149');
4038              }              }
4039    
4040              ## "after head" insertion mode              ## "after head" insertion mode
4041              ## As if <body>              ## As if <body>
4042              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4043              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4044              ## reprocess              ## reprocess
4045              redo B;              redo B;
4046            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4047              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4048            }            !!!cp ('t149.1');
4049    
4050              ## NOTE: As if <head>
4051              !!!create-element ($self->{head_element}, 'head',, $token);
4052              $self->{open_elements}->[-1]->[0]->append_child
4053                  ($self->{head_element});
4054              #push @{$self->{open_elements}},
4055              #    [$self->{head_element}, $el_category->{head}];
4056              #$self->{insertion_mode} = IN_HEAD_IM;
4057              ## NOTE: Reprocess.
4058    
4059              ## NOTE: As if </head>
4060              #pop @{$self->{open_elements}};
4061              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4062              ## NOTE: Reprocess.
4063              
4064              #
4065            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4066              !!!cp ('t149.2');
4067    
4068              ## NOTE: As if </head>
4069              pop @{$self->{open_elements}};
4070              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4071              ## NOTE: Reprocess.
4072    
4073              #
4074            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4075              !!!cp ('t149.3');
4076    
4077              !!!parse-error (type => 'in noscript:#eof', token => $token);
4078    
4079              ## As if </noscript>
4080              pop @{$self->{open_elements}};
4081              #$self->{insertion_mode} = IN_HEAD_IM;
4082              ## NOTE: Reprocess.
4083    
4084              ## NOTE: As if </head>
4085              pop @{$self->{open_elements}};
4086              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4087              ## NOTE: Reprocess.
4088    
4089              #
4090            } else {
4091              !!!cp ('t149.4');
4092              #
4093            }
4094    
4095            ## NOTE: As if <body>
4096            !!!insert-element ('body',, $token);
4097            $self->{insertion_mode} = IN_BODY_IM;
4098            ## NOTE: Reprocess.
4099            redo B;
4100          } else {
4101            die "$0: $token->{type}: Unknown token type";
4102          }
4103    
4104            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
4105      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
4106            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
4107                !!!cp ('t150');
4108              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
4109              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
4110                            
# Line 3312  sub _tree_construction_main ($) { Line 4119  sub _tree_construction_main ($) {
4119                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4120                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
4121                  ## have an element in table scope                  ## have an element in table scope
4122                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
4123                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4124                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
4125                      $tn = $node->[1];                      !!!cp ('t151');
4126                      last INSCOPE;  
4127                    } elsif ({                      ## Close the cell
4128                              table => 1, html => 1,                      !!!back-token; # <?>
4129                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
4130                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
4131                    }                                line => $token->{line},
4132                  } # INSCOPE                                column => $token->{column}};
                   unless (defined $tn) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
4133                      redo B;                      redo B;
4134                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4135                        !!!cp ('t152');
4136                        ## ISSUE: This case can never be reached, maybe.
4137                        last;
4138                    }                    }
4139                                    }
4140                  ## Close the cell  
4141                  !!!back-token; # <?>                  !!!cp ('t153');
4142                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
4143                        value => $token->{tag_name}, token => $token);
4144                    ## Ignore the token
4145                    !!!next-token;
4146                  redo B;                  redo B;
4147                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4148                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed:caption', token => $token);
4149                                    
4150                  ## As if </caption>                  ## NOTE: As if </caption>.
4151                  ## have a table element in table scope                  ## have a table element in table scope
4152                  my $i;                  my $i;
4153                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
4154                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
4155                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
4156                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
4157                      last INSCOPE;                        !!!cp ('t155');
4158                    } elsif ({                        $i = $_;
4159                              table => 1, html => 1,                        last INSCOPE;
4160                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4161                      last INSCOPE;                        !!!cp ('t156');
4162                          last;
4163                        }
4164                    }                    }
4165    
4166                      !!!cp ('t157');
4167                      !!!parse-error (type => 'start tag not allowed',
4168                                      value => $token->{tag_name}, token => $token);
4169                      ## Ignore the token
4170                      !!!next-token;
4171                      redo B;
4172                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
4173                                    
4174                  ## generate implied end tags                  ## generate implied end tags
4175                  if ({                  while ($self->{open_elements}->[-1]->[1]
4176                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
4177                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
4178                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token; # <?>  
                   $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
4179                  }                  }
4180    
4181                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4182                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
4183                      !!!parse-error (type => 'not closed',
4184                                      value => $self->{open_elements}->[-1]->[0]
4185                                          ->manakai_local_name,
4186                                      token => $token);
4187                    } else {
4188                      !!!cp ('t160');
4189                  }                  }
4190                                    
4191                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3386  sub _tree_construction_main ($) { Line 4197  sub _tree_construction_main ($) {
4197                  ## reprocess                  ## reprocess
4198                  redo B;                  redo B;
4199                } else {                } else {
4200                    !!!cp ('t161');
4201                  #                  #
4202                }                }
4203              } else {              } else {
4204                  !!!cp ('t162');
4205                #                #
4206              }              }
4207            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3398  sub _tree_construction_main ($) { Line 4211  sub _tree_construction_main ($) {
4211                  my $i;                  my $i;
4212                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4213                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4214                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4215                        !!!cp ('t163');
4216                      $i = $_;                      $i = $_;
4217                      last INSCOPE;                      last INSCOPE;
4218                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4219                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
4220                      last INSCOPE;                      last INSCOPE;
4221                    }                    }
4222                  } # INSCOPE                  } # INSCOPE
4223                    unless (defined $i) {                    unless (defined $i) {
4224                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
4225                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4226                      ## Ignore the token                      ## Ignore the token
4227                      !!!next-token;                      !!!next-token;
4228                      redo B;                      redo B;
4229                    }                    }
4230                                    
4231                  ## generate implied end tags                  ## generate implied end tags
4232                  if ({                  while ($self->{open_elements}->[-1]->[1]
4233                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
4234                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
4235                       th => ($token->{tag_name} eq 'td'),                    pop @{$self->{open_elements}};
                      tr => 1,  
                      tbody => 1, tfoot=> 1, thead => 1,  
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
4236                  }                  }
4237                    
4238                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
4239                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
4240                      !!!cp ('t167');
4241                      !!!parse-error (type => 'not closed',
4242                                      value => $self->{open_elements}->[-1]->[0]
4243                                          ->manakai_local_name,
4244                                      token => $token);
4245                    } else {
4246                      !!!cp ('t168');
4247                  }                  }
4248                                    
4249                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3441  sub _tree_construction_main ($) { Line 4255  sub _tree_construction_main ($) {
4255                  !!!next-token;                  !!!next-token;
4256                  redo B;                  redo B;
4257                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4258                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
4259                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4260                  ## Ignore the token                  ## Ignore the token
4261                  !!!next-token;                  !!!next-token;
4262                  redo B;                  redo B;
4263                } else {                } else {
4264                    !!!cp ('t170');
4265                  #                  #
4266                }                }
4267              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
4268                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
4269                  ## have a table element in table scope                  ## have a table element in table scope
4270                  my $i;                  my $i;
4271                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
4272                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
4273                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
4274                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
4275                      last INSCOPE;                        !!!cp ('t171');
4276                    } elsif ({                        $i = $_;
4277                              table => 1, html => 1,                        last INSCOPE;
4278                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4279                      last INSCOPE;                        !!!cp ('t172');
4280                          last;
4281                        }
4282                    }                    }
4283    
4284                      !!!cp ('t173');
4285                      !!!parse-error (type => 'unmatched end tag',
4286                                      value => $token->{tag_name}, token => $token);
4287                      ## Ignore the token
4288                      !!!next-token;
4289                      redo B;
4290                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
4291                                    
4292                  ## generate implied end tags                  ## generate implied end tags
4293                  if ({                  while ($self->{open_elements}->[-1]->[1]
4294                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
4295                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
4296                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
4297                  }                  }
4298                                    
4299                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4300                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
4301                      !!!parse-error (type => 'not closed',
4302                                      value => $self->{open_elements}->[-1]->[0]
4303                                          ->manakai_local_name,
4304                                      token => $token);
4305                    } else {
4306                      !!!cp ('t176');
4307                  }                  }
4308                                    
4309                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3495  sub _tree_construction_main ($) { Line 4315  sub _tree_construction_main ($) {
4315                  !!!next-token;                  !!!next-token;
4316                  redo B;                  redo B;
4317                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
4318                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
4319                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4320                  ## Ignore the token                  ## Ignore the token
4321                  !!!next-token;                  !!!next-token;
4322                  redo B;                  redo B;
4323                } else {                } else {
4324                    !!!cp ('t178');
4325                  #                  #
4326                }                }
4327              } elsif ({              } elsif ({
# Line 3510  sub _tree_construction_main ($) { Line 4332  sub _tree_construction_main ($) {
4332                ## have an element in table scope                ## have an element in table scope
4333                my $i;                my $i;
4334                my $tn;                my $tn;
4335                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
4336                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
4337                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
4338                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4339                    last INSCOPE;                      !!!cp ('t179');
4340                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
4341                    $tn = $node->[1];  
4342                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
4343                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </?>
4344                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
4345                            table => 1, html => 1,                                line => $token->{line},
4346                           }->{$node->[1]}) {                                column => $token->{column}};
4347                    last INSCOPE;                      redo B;
4348                      } elsif ($node->[1] & TABLE_CELL_EL) {
4349                        !!!cp ('t180');
4350                        $tn = $node->[0]->manakai_local_name;
4351                        ## NOTE: There is exactly one |td| or |th| element
4352                        ## in scope in the stack of open elements by definition.
4353                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4354                        ## ISSUE: Can this be reached?
4355                        !!!cp ('t181');
4356                        last;
4357                      }
4358                  }                  }
4359                } # INSCOPE  
4360                unless (defined $i) {                  !!!cp ('t182');
4361                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
4362                        value => $token->{tag_name}, token => $token);
4363                  ## Ignore the token                  ## Ignore the token
4364                  !!!next-token;                  !!!next-token;
4365                  redo B;                  redo B;
4366                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
4367              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
4368                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
4369                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed:caption', token => $token);
4370    
4371                ## As if </caption>                ## As if </caption>
4372                ## have a table element in table scope                ## have a table element in table scope
4373                my $i;                my $i;
4374                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4375                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4376                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
4377                      !!!cp ('t184');
4378                    $i = $_;                    $i = $_;
4379                    last INSCOPE;                    last INSCOPE;
4380                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4381                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
4382                    last INSCOPE;                    last INSCOPE;
4383                  }                  }
4384                } # INSCOPE                } # INSCOPE
4385                unless (defined $i) {                unless (defined $i) {
4386                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
4387                    !!!parse-error (type => 'unmatched end tag:caption', token => $token);
4388                  ## Ignore the token                  ## Ignore the token
4389                  !!!next-token;                  !!!next-token;
4390                  redo B;                  redo B;
4391                }                }
4392                                
4393                ## generate implied end tags                ## generate implied end tags
4394                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4395                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
4396                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
4397                }                }
4398    
4399                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4400                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
4401                    !!!parse-error (type => 'not closed',
4402                                    value => $self->{open_elements}->[-1]->[0]
4403                                        ->manakai_local_name,
4404                                    token => $token);
4405                  } else {
4406                    !!!cp ('t189');
4407                }                }
4408    
4409                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3591  sub _tree_construction_main ($) { Line 4418  sub _tree_construction_main ($) {
4418                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
4419                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4420                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
4421                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
4422                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4423                  ## Ignore the token                  ## Ignore the token
4424                  !!!next-token;                  !!!next-token;
4425                  redo B;                  redo B;
4426                } else {                } else {
4427                    !!!cp ('t191');
4428                  #                  #
4429                }                }
4430              } elsif ({              } elsif ({
# Line 3603  sub _tree_construction_main ($) { Line 4432  sub _tree_construction_main ($) {
4432                        thead => 1, tr => 1,                        thead => 1, tr => 1,
4433                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
4434                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
4435                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
4436                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4437                ## Ignore the token                ## Ignore the token
4438                !!!next-token;                !!!next-token;
4439                redo B;                redo B;
4440              } else {              } else {
4441                  !!!cp ('t193');
4442                #                #
4443              }              }
4444          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4445            for my $entry (@{$self->{open_elements}}) {
4446              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
4447                !!!cp ('t75');
4448                !!!parse-error (type => 'in body:#eof', token => $token);
4449                last;
4450              }
4451            }
4452    
4453            ## Stop parsing.
4454            last B;
4455        } else {        } else {
4456          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4457        }        }
# Line 3618  sub _tree_construction_main ($) { Line 4460  sub _tree_construction_main ($) {
4460        #        #
4461      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
4462        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4463              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
4464                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4465              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4466                                
4467                unless (length $token->{data}) {            unless (length $token->{data}) {
4468                  !!!next-token;              !!!cp ('t194');
4469                  redo B;              !!!next-token;
4470                }              redo B;
4471              }            } else {
4472                !!!cp ('t195');
4473              }
4474            }
4475    
4476              !!!parse-error (type => 'in table:#character');              !!!parse-error (type => 'in table:#character', token => $token);
4477    
4478              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
4479              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 3635  sub _tree_construction_main ($) { Line 4481  sub _tree_construction_main ($) {
4481              ## result in a new Text node.              ## result in a new Text node.
4482              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
4483                            
4484              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
4485                # MUST                # MUST
4486                my $foster_parent_element;                my $foster_parent_element;
4487                my $next_sibling;                my $next_sibling;
4488                my $prev_sibling;                my $prev_sibling;
4489                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
4490                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4491                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4492                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
4493                        !!!cp ('t196');
4494                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
4495                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
4496                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
4497                    } else {                    } else {
4498                        !!!cp ('t197');
4499                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
4500                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
4501                    }                    }
# Line 3662  sub _tree_construction_main ($) { Line 4507  sub _tree_construction_main ($) {
4507                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
4508                if (defined $prev_sibling and                if (defined $prev_sibling and
4509                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
4510                    !!!cp ('t198');
4511                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
4512                } else {                } else {
4513                    !!!cp ('t199');
4514                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
4515                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
4516                     $next_sibling);                     $next_sibling);
4517                }                }
4518              } else {            $open_tables->[-1]->[1] = 1; # tainted
4519                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
4520              }            !!!cp ('t200');
4521              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4522            }
4523                            
4524              !!!next-token;          !!!next-token;
4525              redo B;          redo B;
4526        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4527              if ({              if ({
4528                   tr => ($self->{insertion_mode} != IN_ROW_IM),                   tr => ($self->{insertion_mode} != IN_ROW_IM),
# Line 3681  sub _tree_construction_main ($) { Line 4530  sub _tree_construction_main ($) {
4530                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4531                if ($self->{insertion_mode} == IN_TABLE_IM) {                if ($self->{insertion_mode} == IN_TABLE_IM) {
4532                  ## Clear back to table context                  ## Clear back to table context
4533                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4534                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4535                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t201');
4536                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4537                  }                  }
4538                                    
4539                  !!!insert-element ('tbody');                  !!!insert-element ('tbody',, $token);
4540                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
4541                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
4542                }                }
4543    
4544                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4545                  unless ($token->{tag_name} eq 'tr') {                  unless ($token->{tag_name} eq 'tr') {
4546                    !!!parse-error (type => 'missing start tag:tr');                    !!!cp ('t202');
4547                      !!!parse-error (type => 'missing start tag:tr', token => $token);
4548                  }                  }
4549                                    
4550                  ## Clear back to table body context                  ## Clear back to table body context
4551                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4552                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
4553                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t203');
4554                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
4555                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4556                  }                  }
4557                                    
4558                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
4559                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
4560                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!cp ('t204');
4561                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4562                    !!!next-token;                    !!!next-token;
4563                    redo B;                    redo B;
4564                  } else {                  } else {
4565                    !!!insert-element ('tr');                    !!!cp ('t205');
4566                      !!!insert-element ('tr',, $token);
4567                    ## reprocess in the "in row" insertion mode                    ## reprocess in the "in row" insertion mode
4568                  }                  }
4569                  } else {
4570                    !!!cp ('t206');
4571                }                }
4572    
4573                ## Clear back to table row context                ## Clear back to table row context
4574                while (not {                while (not ($self->{open_elements}->[-1]->[1]
4575                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
4576                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4577                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4578                }                }
4579                                
4580                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4581                $self->{insertion_mode} = IN_CELL_IM;                $self->{insertion_mode} = IN_CELL_IM;
4582    
4583                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
# Line 3742  sub _tree_construction_main ($) { Line 4595  sub _tree_construction_main ($) {
4595                  my $i;                  my $i;
4596                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4597                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4598                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
4599                        !!!cp ('t208');
4600                      $i = $_;                      $i = $_;
4601                      last INSCOPE;                      last INSCOPE;
4602                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4603                              table => 1, html => 1,                      !!!cp ('t209');
                            }->{$node->[1]}) {  
4604                      last INSCOPE;                      last INSCOPE;
4605                    }                    }
4606                  } # INSCOPE                  } # INSCOPE
4607                  unless (defined $i) {                  unless (defined $i) {
4608                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                   !!!cp ('t210');
4609    ## TODO: This type is wrong.
4610                     !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);
4611                    ## Ignore the token                    ## Ignore the token
4612                    !!!next-token;                    !!!next-token;
4613                    redo B;                    redo B;
4614                  }                  }
4615                                    
4616                  ## Clear back to table row context                  ## Clear back to table row context
4617                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4618                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
4619                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
4620                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
4621                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4622                  }                  }
4623                                    
4624                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
4625                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
4626                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
4627                      !!!cp ('t212');
4628                    ## reprocess                    ## reprocess
4629                    redo B;                    redo B;
4630                  } else {                  } else {
4631                      !!!cp ('t213');
4632                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
4633                  }                  }
4634                }                }
# Line 3781  sub _tree_construction_main ($) { Line 4638  sub _tree_construction_main ($) {
4638                  my $i;                  my $i;
4639                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4640                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4641                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
4642                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
4643                      $i = $_;                      $i = $_;
4644                      last INSCOPE;                      last INSCOPE;
4645                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4646                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
4647                      last INSCOPE;                      last INSCOPE;
4648                    }                    }
4649                  } # INSCOPE                  } # INSCOPE
4650                  unless (defined $i) {                  unless (defined $i) {
4651                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
4652    ## TODO: This erorr type ios wrong.
4653                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4654                    ## Ignore the token                    ## Ignore the token
4655                    !!!next-token;                    !!!next-token;
4656                    redo B;                    redo B;
4657                  }                  }
4658    
4659                  ## Clear back to table body context                  ## Clear back to table body context
4660                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4661                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
4662                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
4663                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
4664                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4665                  }                  }
4666                                    
# Line 3817  sub _tree_construction_main ($) { Line 4674  sub _tree_construction_main ($) {
4674                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4675                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
4676                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
4677                  } else {
4678                    !!!cp ('t218');
4679                }                }
4680    
4681                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
4682                  ## Clear back to table context                  ## Clear back to table context
4683                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4684                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4685                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t219');
4686                      ## ISSUE: Can this state be reached?
4687                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4688                  }                  }
4689                                    
4690                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup',, $token);
4691                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
4692                  ## reprocess                  ## reprocess
4693                  redo B;                  redo B;
# Line 3837  sub _tree_construction_main ($) { Line 4697  sub _tree_construction_main ($) {
4697                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
4698                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
4699                  ## Clear back to table context                  ## Clear back to table context
4700                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4701                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4702                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t220');
4703                      ## ISSUE: Can this state be reached?
4704                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4705                  }                  }
4706                                    
4707                  push @$active_formatting_elements, ['#marker', '']                  push @$active_formatting_elements, ['#marker', '']
4708                      if $token->{tag_name} eq 'caption';                      if $token->{tag_name} eq 'caption';
4709                                    
4710                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4711                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
4712                                             caption => IN_CAPTION_IM,                                             caption => IN_CAPTION_IM,
4713                                             colgroup => IN_COLUMN_GROUP_IM,                                             colgroup => IN_COLUMN_GROUP_IM,
# Line 3860  sub _tree_construction_main ($) { Line 4721  sub _tree_construction_main ($) {
4721                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
4722                }                }
4723              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4724                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
4725                                  value => $self->{open_elements}->[-1]->[0]
4726                                      ->manakai_local_name,
4727                                  token => $token);
4728    
4729                ## As if </table>                ## As if </table>
4730                ## have a table element in table scope                ## have a table element in table scope
4731                my $i;                my $i;
4732                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4733                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4734                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
4735                      !!!cp ('t221');
4736                    $i = $_;                    $i = $_;
4737                    last INSCOPE;                    last INSCOPE;
4738                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4739                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
4740                    last INSCOPE;                    last INSCOPE;
4741                  }                  }
4742                } # INSCOPE                } # INSCOPE
4743                unless (defined $i) {                unless (defined $i) {
4744                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
4745    ## TODO: The following is wrong, maybe.
4746                    !!!parse-error (type => 'unmatched end tag:table', token => $token);
4747                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
4748                  !!!next-token;                  !!!next-token;
4749                  redo B;                  redo B;
4750                }                }
4751                                
4752    ## TODO: Followings are removed from the latest spec.
4753                ## generate implied end tags                ## generate implied end tags
4754                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4755                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
4756                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
4757                }                }
4758    
4759                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
4760                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
4761                    ## NOTE: |<table><tr><table>|
4762                    !!!parse-error (type => 'not closed',
4763                                    value => $self->{open_elements}->[-1]->[0]
4764                                        ->manakai_local_name,
4765                                    token => $token);
4766                  } else {
4767                    !!!cp ('t226');
4768                }                }
4769    
4770                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4771                  pop @{$open_tables};
4772    
4773                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
4774    
4775                ## reprocess                ## reprocess
4776                redo B;                redo B;
4777          } else {          } elsif ($token->{tag_name} eq 'style') {
4778            !!!parse-error (type => 'in table:'.$token->{tag_name});            if (not $open_tables->[-1]->[1]) { # tainted
4779                !!!cp ('t227.8');
4780                ## NOTE: This is a "as if in head" code clone.
4781                $parse_rcdata->(CDATA_CONTENT_MODEL);
4782                redo B;
4783              } else {
4784                !!!cp ('t227.7');
4785                #
4786              }
4787            } elsif ($token->{tag_name} eq 'script') {
4788              if (not $open_tables->[-1]->[1]) { # tainted
4789                !!!cp ('t227.6');
4790                ## NOTE: This is a "as if in head" code clone.
4791                $script_start_tag->();
4792                redo B;
4793              } else {
4794                !!!cp ('t227.5');
4795                #
4796              }
4797            } elsif ($token->{tag_name} eq 'input') {
4798              if (not $open_tables->[-1]->[1]) { # tainted
4799                if ($token->{attributes}->{type}) { ## TODO: case
4800                  my $type = lc $token->{attributes}->{type}->{value};
4801                  if ($type eq 'hidden') {
4802                    !!!cp ('t227.3');
4803                    !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4804    
4805            $insert = $insert_to_foster;                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4806    
4807                    ## TODO: form element pointer
4808    
4809                    pop @{$self->{open_elements}};
4810    
4811                    !!!next-token;
4812                    redo B;
4813                  } else {
4814                    !!!cp ('t227.2');
4815                    #
4816                  }
4817                } else {
4818                  !!!cp ('t227.1');
4819                  #
4820                }
4821              } else {
4822                !!!cp ('t227.4');
4823                #
4824              }
4825            } else {
4826              !!!cp ('t227');
4827            #            #
4828          }          }
4829    
4830            !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
4831    
4832            $insert = $insert_to_foster;
4833            #
4834        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4835              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
4836                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 3920  sub _tree_construction_main ($) { Line 4838  sub _tree_construction_main ($) {
4838                my $i;                my $i;
4839                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4840                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4841                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
4842                      !!!cp ('t228');
4843                    $i = $_;                    $i = $_;
4844                    last INSCOPE;                    last INSCOPE;
4845                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4846                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
4847                    last INSCOPE;                    last INSCOPE;
4848                  }                  }
4849                } # INSCOPE                } # INSCOPE
4850                unless (defined $i) {                unless (defined $i) {
4851                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
4852                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4853                  ## Ignore the token                  ## Ignore the token
4854                  !!!next-token;                  !!!next-token;
4855                  redo B;                  redo B;
4856                  } else {
4857                    !!!cp ('t232');
4858                }                }
4859    
4860                ## Clear back to table row context                ## Clear back to table row context
4861                while (not {                while (not ($self->{open_elements}->[-1]->[1]
4862                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
4863                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
4864                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
4865                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4866                }                }
4867    
# Line 3955  sub _tree_construction_main ($) { Line 4876  sub _tree_construction_main ($) {
4876                  my $i;                  my $i;
4877                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4878                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4879                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
4880                        !!!cp ('t233');
4881                      $i = $_;                      $i = $_;
4882                      last INSCOPE;                      last INSCOPE;
4883                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4884                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
4885                      last INSCOPE;                      last INSCOPE;
4886                    }                    }
4887                  } # INSCOPE                  } # INSCOPE
4888                  unless (defined $i) {                  unless (defined $i) {
4889                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
4890    ## TODO: The following is wrong.
4891                      !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);
4892                    ## Ignore the token                    ## Ignore the token
4893                    !!!next-token;                    !!!next-token;
4894                    redo B;                    redo B;
4895                  }                  }
4896                                    
4897                  ## Clear back to table row context                  ## Clear back to table row context
4898                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4899                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
4900                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
4901                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
4902                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4903                  }                  }
4904                                    
# Line 3989  sub _tree_construction_main ($) { Line 4912  sub _tree_construction_main ($) {
4912                  my $i;                  my $i;
4913                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4914                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4915                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
4916                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
4917                      $i = $_;                      $i = $_;
4918                      last INSCOPE;                      last INSCOPE;
4919                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4920                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
4921                      last INSCOPE;                      last INSCOPE;
4922                    }                    }
4923                  } # INSCOPE                  } # INSCOPE
4924                  unless (defined $i) {                  unless (defined $i) {
4925                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
4926                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4927                    ## Ignore the token                    ## Ignore the token
4928                    !!!next-token;                    !!!next-token;
4929                    redo B;                    redo B;
4930                  }                  }
4931                                    
4932                  ## Clear back to table body context                  ## Clear back to table body context
4933                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4934                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
4935                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4936                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4937                  }                  }
4938                                    
# Line 4027  sub _tree_construction_main ($) { Line 4948  sub _tree_construction_main ($) {
4948                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
4949                }                }
4950    
4951                  ## NOTE: </table> in the "in table" insertion mode.
4952                  ## When you edit the code fragment below, please ensure that
4953                  ## the code for <table> in the "in table" insertion mode
4954                  ## is synced with it.
4955    
4956                ## have a table element in table scope                ## have a table element in table scope
4957                my $i;                my $i;
4958                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4959                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4960                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
4961                      !!!cp ('t241');
4962                    $i = $_;                    $i = $_;
4963                    last INSCOPE;                    last INSCOPE;
4964                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
4965                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
4966                    last INSCOPE;                    last INSCOPE;
4967                  }                  }
4968                } # INSCOPE                } # INSCOPE
4969                unless (defined $i) {                unless (defined $i) {
4970                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
4971                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4972                  ## Ignore the token                  ## Ignore the token
4973                  !!!next-token;                  !!!next-token;
4974                  redo B;                  redo B;
4975                }                }
   
               ## 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_TOKEN,  
                           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]);  
               }  
4976                                    
4977                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4978                  pop @{$open_tables};
4979                                
4980                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
4981                                
# Line 4078  sub _tree_construction_main ($) { Line 4990  sub _tree_construction_main ($) {
4990                  my $i;                  my $i;
4991                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4992                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4993                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4994                        !!!cp ('t247');
4995                      $i = $_;                      $i = $_;
4996                      last INSCOPE;                      last INSCOPE;
4997                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
4998                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
4999                      last INSCOPE;                      last INSCOPE;
5000                    }                    }
5001                  } # INSCOPE                  } # INSCOPE
5002                    unless (defined $i) {                    unless (defined $i) {
5003                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
5004                        !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5005                      ## Ignore the token                      ## Ignore the token
5006                      !!!next-token;                      !!!next-token;
5007                      redo B;                      redo B;
# Line 4099  sub _tree_construction_main ($) { Line 5012  sub _tree_construction_main ($) {
5012                  my $i;                  my $i;
5013                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5014                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5015                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5016                        !!!cp ('t250');
5017                      $i = $_;                      $i = $_;
5018                      last INSCOPE;                      last INSCOPE;
5019                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5020                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
5021                      last INSCOPE;                      last INSCOPE;
5022                    }                    }
5023                  } # INSCOPE                  } # INSCOPE
5024                    unless (defined $i) {                    unless (defined $i) {
5025                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
5026                        !!!parse-error (type => 'unmatched end tag:tr', token => $token);
5027                      ## Ignore the token                      ## Ignore the token
5028                      !!!next-token;                      !!!next-token;
5029                      redo B;                      redo B;
5030                    }                    }
5031                                    
5032                  ## Clear back to table row context                  ## Clear back to table row context
5033                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5034                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5035                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
5036                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
5037                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5038                  }                  }
5039                                    
# Line 4132  sub _tree_construction_main ($) { Line 5046  sub _tree_construction_main ($) {
5046                my $i;                my $i;
5047                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5048                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5049                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5050                      !!!cp ('t254');
5051                    $i = $_;                    $i = $_;
5052                    last INSCOPE;                    last INSCOPE;
5053                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5054                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
5055                    last INSCOPE;                    last INSCOPE;
5056                  }                  }
5057                } # INSCOPE                } # INSCOPE
5058                unless (defined $i) {                unless (defined $i) {
5059                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
5060                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5061                  ## Ignore the token                  ## Ignore the token
5062                  !!!next-token;                  !!!next-token;
5063                  redo B;                  redo B;
5064                }                }
5065    
5066                ## Clear back to table body context                ## Clear back to table body context
5067                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5068                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
5069                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
5070                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
5071                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5072                }                }
5073    
# Line 4166  sub _tree_construction_main ($) { Line 5081  sub _tree_construction_main ($) {
5081                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5082                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
5083                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5084                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t258');
5085                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5086                ## Ignore the token                ## Ignore the token
5087                !!!next-token;                !!!next-token;
5088                redo B;                redo B;
5089          } else {          } else {
5090            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!cp ('t259');
5091              !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);
5092    
5093            $insert = $insert_to_foster;            $insert = $insert_to_foster;
5094            #            #
5095          }          }
5096          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5097            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5098                    @{$self->{open_elements}} == 1) { # redundant, maybe
5099              !!!parse-error (type => 'in body:#eof', token => $token);
5100              !!!cp ('t259.1');
5101              #
5102            } else {
5103              !!!cp ('t259.2');
5104              #
5105            }
5106    
5107            ## Stop parsing
5108            last B;
5109        } else {        } else {
5110          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5111        }        }
# Line 4184  sub _tree_construction_main ($) { Line 5114  sub _tree_construction_main ($) {
5114              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5115                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5116                unless (length $token->{data}) {                unless (length $token->{data}) {
5117                    !!!cp ('t260');
5118                  !!!next-token;                  !!!next-token;
5119                  redo B;                  redo B;
5120                }                }
5121              }              }
5122                            
5123                !!!cp ('t261');
5124              #              #
5125            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5126              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
5127                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
5128                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5129                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5130                !!!next-token;                !!!next-token;
5131                redo B;                redo B;
5132              } else {              } else {
5133                  !!!cp ('t263');
5134                #                #
5135              }              }
5136            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
5137              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
5138                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5139                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
5140                    !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5141                  ## Ignore the token                  ## Ignore the token
5142                  !!!next-token;                  !!!next-token;
5143                  redo B;                  redo B;
5144                } else {                } else {
5145                    !!!cp ('t265');
5146                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
5147                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5148                  !!!next-token;                  !!!next-token;
5149                  redo B;                              redo B;            
5150                }                }
5151              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
5152                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
5153                  !!!parse-error (type => 'unmatched end tag:col', token => $token);
5154                ## Ignore the token                ## Ignore the token
5155                !!!next-token;                !!!next-token;
5156                redo B;                redo B;
5157              } else {              } else {
5158                  !!!cp ('t267');
5159                #                #
5160              }              }
5161            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5162              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5163            }              @{$self->{open_elements}} == 1) { # redundant, maybe
5164              !!!cp ('t270.2');
5165              ## Stop parsing.
5166              last B;
5167            } else {
5168              ## NOTE: As if </colgroup>.
5169              !!!cp ('t270.1');
5170              pop @{$self->{open_elements}}; # colgroup
5171              $self->{insertion_mode} = IN_TABLE_IM;
5172              ## Reprocess.
5173              redo B;
5174            }
5175          } else {
5176            die "$0: $token->{type}: Unknown token type";
5177          }
5178    
5179            ## As if </colgroup>            ## As if </colgroup>
5180            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5181              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
5182    ## TODO: Wrong error type?
5183                !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5184              ## Ignore the token              ## Ignore the token
5185              !!!next-token;              !!!next-token;
5186              redo B;              redo B;
5187            } else {            } else {
5188                !!!cp ('t270');
5189              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
5190              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
5191              ## reprocess              ## reprocess
5192              redo B;              redo B;
5193            }            }
5194      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
5195        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5196            !!!cp ('t271');
5197          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5198          !!!next-token;          !!!next-token;
5199          redo B;          redo B;
5200        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5201              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
5202                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5203                  ## As if </option>              !!!cp ('t272');
5204                  pop @{$self->{open_elements}};              ## As if </option>
5205                }              pop @{$self->{open_elements}};
5206              } else {
5207                !!!cp ('t273');
5208              }
5209    
5210                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5211                !!!next-token;            !!!next-token;
5212                redo B;            redo B;
5213              } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
5214                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5215                  ## As if </option>              !!!cp ('t274');
5216                  pop @{$self->{open_elements}};              ## As if </option>
5217                }              pop @{$self->{open_elements}};
5218              } else {
5219                !!!cp ('t275');
5220              }
5221    
5222                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5223                  ## As if </optgroup>              !!!cp ('t276');
5224                  pop @{$self->{open_elements}};              ## As if </optgroup>
5225                }              pop @{$self->{open_elements}};
5226              } else {
5227                !!!cp ('t277');
5228              }
5229    
5230                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5231                !!!next-token;            !!!next-token;
5232                redo B;            redo B;
5233              } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select' or
5234                !!!parse-error (type => 'not closed:select');                   $token->{tag_name} eq 'input' or
5235                ## As if </select> instead                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5236                ## have an element in table scope                    {
5237                my $i;                     caption => 1, table => 1,
5238                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     tbody => 1, tfoot => 1, thead => 1,
5239                  my $node = $self->{open_elements}->[$_];                     tr => 1, td => 1, th => 1,
5240                  if ($node->[1] eq $token->{tag_name}) {                    }->{$token->{tag_name}})) {
5241                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
5242                    last INSCOPE;            !!!parse-error (type => 'not closed:select', token => $token);
5243                  } elsif ({            ## NOTE: As if the token were </select> (<select> case) or
5244                            table => 1, html => 1,            ## as if there were </select> (otherwise).
5245                           }->{$node->[1]}) {            ## have an element in table scope
5246                    last INSCOPE;            my $i;
5247                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5248                } # INSCOPE              my $node = $self->{open_elements}->[$_];
5249                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
5250                  !!!parse-error (type => 'unmatched end tag:select');                !!!cp ('t278');
5251                  ## Ignore the token                $i = $_;
5252                  !!!next-token;                last INSCOPE;
5253                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5254                }                !!!cp ('t279');
5255                  last INSCOPE;
5256                }
5257              } # INSCOPE
5258              unless (defined $i) {
5259                !!!cp ('t280');
5260                !!!parse-error (type => 'unmatched end tag:select', token => $token);
5261                ## Ignore the token
5262                !!!next-token;
5263                redo B;
5264              }
5265                                
5266                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
5267              splice @{$self->{open_elements}}, $i;
5268    
5269                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5270    
5271                !!!next-token;            if ($token->{tag_name} eq 'select') {
5272                redo B;              !!!cp ('t281.2');
5273                !!!next-token;
5274                redo B;
5275              } else {
5276                !!!cp ('t281.1');
5277                ## Reprocess the token.
5278                redo B;
5279              }
5280          } else {          } else {
5281            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!cp ('t282');
5282              !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);
5283            ## Ignore the token            ## Ignore the token
5284            !!!next-token;            !!!next-token;
5285            redo B;            redo B;
5286          }          }
5287        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5288              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
5289                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
5290                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
5291                  ## As if </option>              !!!cp ('t283');
5292                  splice @{$self->{open_elements}}, -2;              ## As if </option>
5293                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              splice @{$self->{open_elements}}, -2;
5294                  pop @{$self->{open_elements}};            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5295                } else {              !!!cp ('t284');
5296                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              pop @{$self->{open_elements}};
5297                  ## Ignore the token            } else {
5298                }              !!!cp ('t285');
5299                !!!next-token;              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5300                redo B;              ## Ignore the token
5301              } elsif ($token->{tag_name} eq 'option') {            }
5302                if ($self->{open_elements}->[-1]->[1] eq 'option') {            !!!next-token;
5303                  pop @{$self->{open_elements}};            redo B;
5304                } else {          } elsif ($token->{tag_name} eq 'option') {
5305                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5306                  ## Ignore the token              !!!cp ('t286');
5307                }              pop @{$self->{open_elements}};
5308                !!!next-token;            } else {
5309                redo B;              !!!cp ('t287');
5310              } elsif ($token->{tag_name} eq 'select') {              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5311                ## have an element in table scope              ## Ignore the token
5312                my $i;            }
5313                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            !!!next-token;
5314                  my $node = $self->{open_elements}->[$_];            redo B;
5315                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'select') {
5316                    $i = $_;            ## have an element in table scope
5317                    last INSCOPE;            my $i;
5318                  } elsif ({            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5319                            table => 1, html => 1,              my $node = $self->{open_elements}->[$_];
5320                           }->{$node->[1]}) {              if ($node->[1] & SELECT_EL) {
5321                    last INSCOPE;                !!!cp ('t288');
5322                  }                $i = $_;
5323                } # INSCOPE                last INSCOPE;
5324                unless (defined $i) {              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5325                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t289');
5326                  ## Ignore the token                last INSCOPE;
5327                  !!!next-token;              }
5328                  redo B;            } # INSCOPE
5329                }            unless (defined $i) {
5330                !!!cp ('t290');
5331                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5332                ## Ignore the token
5333                !!!next-token;
5334                redo B;
5335              }
5336                                
5337                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
5338              splice @{$self->{open_elements}}, $i;
5339    
5340                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5341    
5342                !!!next-token;            !!!next-token;
5343                redo B;            redo B;
5344              } elsif ({          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5345                        caption => 1, table => 1, tbody => 1,                   {
5346                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    caption => 1, table => 1, tbody => 1,
5347                       }->{$token->{tag_name}}) {                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
5348                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                   }->{$token->{tag_name}}) {
5349    ## TODO: The following is wrong?
5350              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5351                                
5352                ## have an element in table scope            ## have an element in table scope
5353                my $i;            my $i;
5354                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5355                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
5356                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5357                    $i = $_;                !!!cp ('t292');
5358                    last INSCOPE;                $i = $_;
5359                  } elsif ({                last INSCOPE;
5360                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5361                           }->{$node->[1]}) {                !!!cp ('t293');
5362                    last INSCOPE;                last INSCOPE;
5363                  }              }
5364                } # INSCOPE            } # INSCOPE
5365                unless (defined $i) {            unless (defined $i) {
5366                  ## Ignore the token              !!!cp ('t294');
5367                  !!!next-token;              ## Ignore the token
5368                  redo B;              !!!next-token;
5369                }              redo B;
5370              }
5371                                
5372                ## As if </select>            ## As if </select>
5373                ## have an element in table scope            ## have an element in table scope
5374                undef $i;            undef $i;
5375                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5376                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
5377                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
5378                    $i = $_;                !!!cp ('t295');
5379                    last INSCOPE;                $i = $_;
5380                  } elsif ({                last INSCOPE;
5381                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5382                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
5383                    last INSCOPE;                !!!cp ('t296');
5384                  }                last INSCOPE;
5385                } # INSCOPE              }
5386                unless (defined $i) {            } # INSCOPE
5387                  !!!parse-error (type => 'unmatched end tag:select');            unless (defined $i) {
5388                  ## Ignore the </select> token              !!!cp ('t297');
5389                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
5390                  redo B;              !!!parse-error (type => 'unmatched end tag:select', token => $token);
5391                }              ## Ignore the </select> token
5392                !!!next-token; ## TODO: ok?
5393                redo B;
5394              }
5395                                
5396                splice @{$self->{open_elements}}, $i;            !!!cp ('t298');
5397              splice @{$self->{open_elements}}, $i;
5398    
5399                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5400    
5401                ## reprocess            ## reprocess
5402                redo B;            redo B;
5403          } else {          } else {
5404            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!cp ('t299');
5405              !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);
5406            ## Ignore the token            ## Ignore the token
5407            !!!next-token;            !!!next-token;
5408            redo B;            redo B;
5409          }          }
5410          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5411            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5412                    @{$self->{open_elements}} == 1) { # redundant, maybe
5413              !!!cp ('t299.1');
5414              !!!parse-error (type => 'in body:#eof', token => $token);
5415            } else {
5416              !!!cp ('t299.2');
5417            }
5418    
5419            ## Stop parsing.
5420            last B;
5421        } else {        } else {
5422          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5423        }        }
# Line 4421  sub _tree_construction_main ($) { Line 5431  sub _tree_construction_main ($) {
5431            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5432                        
5433            unless (length $token->{data}) {            unless (length $token->{data}) {
5434                !!!cp ('t300');
5435              !!!next-token;              !!!next-token;
5436              redo B;              redo B;
5437            }            }
5438          }          }
5439                    
5440          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5441            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
5442              !!!parse-error (type => 'after html:#character', token => $token);
5443    
5444            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "after body" insertion mode.
5445            } else {
5446              !!!cp ('t302');
5447          }          }
5448                    
5449          ## "after body" insertion mode          ## "after body" insertion mode
5450          !!!parse-error (type => 'after body:#character');          !!!parse-error (type => 'after body:#character', token => $token);
5451    
5452          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5453          ## reprocess          ## reprocess
5454          redo B;          redo B;
5455        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5456          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5457            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
5458              !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5459                        
5460            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "after body" insertion mode.
5461            } else {
5462              !!!cp ('t304');
5463          }          }
5464    
5465          ## "after body" insertion mode          ## "after body" insertion mode
5466          !!!parse-error (type => 'after body:'.$token->{tag_name});          !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);
5467    
5468          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5469          ## reprocess          ## reprocess
5470          redo B;          redo B;
5471        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5472          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5473            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
5474              !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5475                        
5476            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
5477            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "after body" insertion mode.
5478            } else {
5479              !!!cp ('t306');
5480          }          }
5481    
5482          ## "after body" insertion mode          ## "after body" insertion mode
5483          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
5484            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
5485              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
5486                !!!parse-error (type => 'unmatched end tag:html', token => $token);
5487              ## Ignore the token              ## Ignore the token
5488              !!!next-token;              !!!next-token;
5489              redo B;              redo B;
5490            } else {            } else {
5491                !!!cp ('t308');
5492              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
5493              !!!next-token;              !!!next-token;
5494              redo B;              redo B;
5495            }            }
5496          } else {          } else {
5497            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
5498              !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);
5499    
5500            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
5501            ## reprocess            ## reprocess
5502            redo B;            redo B;
5503          }          }
5504          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5505            !!!cp ('t309.2');
5506            ## Stop parsing
5507            last B;
5508        } else {        } else {
5509          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5510        }        }
# Line 4487  sub _tree_construction_main ($) { Line 5514  sub _tree_construction_main ($) {
5514            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5515                        
5516            unless (length $token->{data}) {            unless (length $token->{data}) {
5517                !!!cp ('t310');
5518              !!!next-token;              !!!next-token;
5519              redo B;              redo B;
5520            }            }
# Line 4494  sub _tree_construction_main ($) { Line 5522  sub _tree_construction_main ($) {
5522                    
5523          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
5524            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5525              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
5526                !!!parse-error (type => 'in frameset:#character', token => $token);
5527            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
5528              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
5529                !!!parse-error (type => 'after frameset:#character', token => $token);
5530            } else { # "after html frameset"            } else { # "after html frameset"
5531              !!!parse-error (type => 'after html:#character');              !!!cp ('t313');
5532                !!!parse-error (type => 'after html:#character', token => $token);
5533    
5534              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5535              ## Reprocess in the "main" phase, "after frameset"...              ## Reprocess in the "after frameset" insertion mode.
5536              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#character', token => $token);
5537            }            }
5538                        
5539            ## Ignore the token.            ## Ignore the token.
5540            if (length $token->{data}) {            if (length $token->{data}) {
5541                !!!cp ('t314');
5542              ## reprocess the rest of characters              ## reprocess the rest of characters
5543            } else {            } else {
5544                !!!cp ('t315');
5545              !!!next-token;              !!!next-token;
5546            }            }
5547            redo B;            redo B;
# Line 4517  sub _tree_construction_main ($) { Line 5550  sub _tree_construction_main ($) {
5550          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
5551        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5552          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5553            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t316');
5554              !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5555    
5556            $self->{insertion_mode} = AFTER_FRAMESET_IM;            $self->{insertion_mode} = AFTER_FRAMESET_IM;
5557            ## Process in the "main" phase, "after frameset" insertion mode...            ## Process in the "after frameset" insertion mode.
5558          }          } else {
5559              !!!cp ('t317');
5560            }
5561    
5562          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
5563              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
5564            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
5565              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5566            !!!next-token;            !!!next-token;
5567            redo B;            redo B;
5568          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
5569                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
5570            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
5571              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5572            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
5573            !!!next-token;            !!!next-token;
5574            redo B;            redo B;
5575          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
5576              !!!cp ('t320');
5577            ## NOTE: As if in body.            ## NOTE: As if in body.
5578            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            $parse_rcdata->(CDATA_CONTENT_MODEL);
5579            redo B;            redo B;
5580          } else {          } else {
5581            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5582              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
5583                !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);
5584            } else {            } else {
5585              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!cp ('t322');
5586                !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);
5587            }            }
5588            ## Ignore the token            ## Ignore the token
5589            !!!next-token;            !!!next-token;
# Line 4550  sub _tree_construction_main ($) { Line 5591  sub _tree_construction_main ($) {
5591          }          }
5592        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5593          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5594            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t323');
5595              !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5596    
5597            $self->{insertion_mode} = AFTER_FRAMESET_IM;            $self->{insertion_mode} = AFTER_FRAMESET_IM;
5598            ## Process in the "main" phase, "after frameset" insertion mode...            ## Process in the "after frameset" insertion mode.
5599            } else {
5600              !!!cp ('t324');
5601          }          }
5602    
5603          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
5604              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
5605            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5606                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
5607              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
5608                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5609              ## Ignore the token              ## Ignore the token
5610              !!!next-token;              !!!next-token;
5611            } else {            } else {
5612                !!!cp ('t326');
5613              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
5614              !!!next-token;              !!!next-token;
5615            }            }
5616    
5617            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
5618                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
5619                !!!cp ('t327');
5620              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5621              } else {
5622                !!!cp ('t328');
5623            }            }
5624            redo B;            redo B;
5625          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
5626                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
5627              !!!cp ('t329');
5628            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
5629            !!!next-token;            !!!next-token;
5630            redo B;            redo B;
5631          } else {          } else {
5632            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5633              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
5634                !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);
5635            } else {            } else {
5636              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!cp ('t331');
5637                !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);
5638            }            }
5639            ## Ignore the token            ## Ignore the token
5640            !!!next-token;            !!!next-token;
5641            redo B;            redo B;
5642          }          }
5643          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5644            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5645                    @{$self->{open_elements}} == 1) { # redundant, maybe
5646              !!!cp ('t331.1');
5647              !!!parse-error (type => 'in body:#eof', token => $token);
5648            } else {
5649              !!!cp ('t331.2');
5650            }
5651            
5652            ## Stop parsing
5653            last B;
5654        } else {        } else {
5655          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5656        }        }
# Line 4600  sub _tree_construction_main ($) { Line 5663  sub _tree_construction_main ($) {
5663      ## "in body" insertion mode      ## "in body" insertion mode
5664      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
5665        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
5666            !!!cp ('t332');
5667          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5668          $script_start_tag->($insert);          $script_start_tag->();
5669          redo B;          redo B;
5670        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
5671            !!!cp ('t333');
5672          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5673          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
5674          redo B;          redo B;
5675        } elsif ({        } elsif ({
5676                  base => 1, link => 1,                  base => 1, link => 1,
5677                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5678            !!!cp ('t334');
5679          ## 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
5680          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5681          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5682          !!!next-token;          !!!next-token;
5683          redo B;          redo B;
5684        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
5685          ## 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
5686          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5687          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5688    
5689          unless ($self->{confident}) {          unless ($self->{confident}) {
5690            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) { ## TODO: And if supported
5691                !!!cp ('t335');
5692              $self->{change_encoding}              $self->{change_encoding}
5693                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
5694                            
5695              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5696                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
# Line 4636  sub _tree_construction_main ($) { Line 5703  sub _tree_construction_main ($) {
5703                      [\x09-\x0D\x20]*=                      [\x09-\x0D\x20]*=
5704                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
5705                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
5706                  !!!cp ('t336');
5707                $self->{change_encoding}                $self->{change_encoding}
5708                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
5709                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5710                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
5711                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 4646  sub _tree_construction_main ($) { Line 5714  sub _tree_construction_main ($) {
5714            }            }
5715          } else {          } else {
5716            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
5717                !!!cp ('t337');
5718              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5719                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
5720                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
5721                                           ->{has_reference});                                           ->{has_reference});
5722            }            }
5723            if ($token->{attributes}->{content}) {            if ($token->{attributes}->{content}) {
5724                !!!cp ('t338');
5725              $meta_el->[0]->get_attribute_node_ns (undef, 'content')              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5726                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
5727                                       $token->{attributes}->{content}                                       $token->{attributes}->{content}
# Line 4662  sub _tree_construction_main ($) { Line 5732  sub _tree_construction_main ($) {
5732          !!!next-token;          !!!next-token;
5733          redo B;          redo B;
5734        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
5735          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
5736          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5737          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
           if (defined $self->{head_element}) {  
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
5738          redo B;          redo B;
5739        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
5740          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body:body', token => $token);
5741                                
5742          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
5743              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
5744              !!!cp ('t342');
5745            ## Ignore the token            ## Ignore the token
5746          } else {          } else {
5747            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
5748            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
5749              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
5750                  !!!cp ('t343');
5751                $body_el->set_attribute_ns                $body_el->set_attribute_ns
5752                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
5753                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
# Line 4692  sub _tree_construction_main ($) { Line 5758  sub _tree_construction_main ($) {
5758          redo B;          redo B;
5759        } elsif ({        } elsif ({
5760                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
5761                  div => 1, dl => 1, fieldset => 1, listing => 1,                  div => 1, dl => 1, fieldset => 1,
5762                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5763                  menu => 1, ol => 1, p => 1, ul => 1,                  menu => 1, ol => 1, p => 1, ul => 1,
5764                  pre => 1,                  pre => 1, listing => 1,
5765                    form => 1,
5766                    table => 1,
5767                    hr => 1,
5768                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5769            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
5770              !!!cp ('t350');
5771              !!!parse-error (type => 'in form:form', token => $token);
5772              ## Ignore the token
5773              !!!next-token;
5774              redo B;
5775            }
5776    
5777          ## has a p element in scope          ## has a p element in scope
5778          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
5779            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
5780                !!!cp ('t344');
5781              !!!back-token;              !!!back-token;
5782              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
5783                          line => $token->{line}, column => $token->{column}};
5784              redo B;              redo B;
5785            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
5786                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t345');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
5787              last INSCOPE;              last INSCOPE;
5788            }            }
5789          } # INSCOPE          } # INSCOPE
5790                        
5791          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5792          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
5793            !!!next-token;            !!!next-token;
5794            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5795              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
5796              unless (length $token->{data}) {              unless (length $token->{data}) {
5797                  !!!cp ('t346');
5798                !!!next-token;                !!!next-token;
5799                } else {
5800                  !!!cp ('t349');
5801              }              }
5802              } else {
5803                !!!cp ('t348');
5804            }            }
5805          } else {          } elsif ($token->{tag_name} eq 'form') {
5806              !!!cp ('t347.1');
5807              $self->{form_element} = $self->{open_elements}->[-1]->[0];
5808    
5809            !!!next-token;            !!!next-token;
5810          }          } elsif ($token->{tag_name} eq 'table') {
5811          redo B;            !!!cp ('t382');
5812        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
5813          if (defined $self->{form_element}) {            
5814            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
5815            ## Ignore the token  
5816              !!!next-token;
5817            } elsif ($token->{tag_name} eq 'hr') {
5818              !!!cp ('t386');
5819              pop @{$self->{open_elements}};
5820            
5821            !!!next-token;            !!!next-token;
           redo B;  
5822          } else {          } else {
5823            ## has a p element in scope            !!!cp ('t347');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
               redo B;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
5824            !!!next-token;            !!!next-token;
           redo B;  
5825          }          }
       } elsif ($token->{tag_name} eq 'li') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
5826          redo B;          redo B;
5827        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
5828          ## has a p element in scope          ## has a p element in scope
5829          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
5830            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
5831                !!!cp ('t353');
5832              !!!back-token;              !!!back-token;
5833              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
5834                          line => $token->{line}, column => $token->{column}};
5835              redo B;              redo B;
5836            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
5837                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t354');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
5838              last INSCOPE;              last INSCOPE;
5839            }            }
5840          } # INSCOPE          } # INSCOPE
# Line 4814  sub _tree_construction_main ($) { Line 5842  sub _tree_construction_main ($) {
5842          ## Step 1          ## Step 1
5843          my $i = -1;          my $i = -1;
5844          my $node = $self->{open_elements}->[$i];          my $node = $self->{open_elements}->[$i];
5845            my $li_or_dtdd = {li => {li => 1},
5846                              dt => {dt => 1, dd => 1},
5847                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
5848          LI: {          LI: {
5849            ## Step 2            ## Step 2
5850            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
5851              if ($i != -1) {              if ($i != -1) {
5852                !!!parse-error (type => 'end tag missing:'.                !!!cp ('t355');
5853                                $self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5854                                  value => $self->{open_elements}->[-1]->[0]
5855                                      ->manakai_local_name,
5856                                  token => $token);
5857                } else {
5858                  !!!cp ('t356');
5859              }              }
5860              splice @{$self->{open_elements}}, $i;              splice @{$self->{open_elements}}, $i;
5861              last LI;              last LI;
5862              } else {
5863                !!!cp ('t357');
5864            }            }
5865                        
5866            ## Step 3            ## Step 3
5867            if (not $formatting_category->{$node->[1]} and            if (not ($node->[1] & FORMATTING_EL) and
5868                #not $phrasing_category->{$node->[1]} and                #not $phrasing_category->{$node->[1]} and
5869                ($special_category->{$node->[1]} or                ($node->[1] & SPECIAL_EL or
5870                 $scoping_category->{$node->[1]}) and                 $node->[1] & SCOPING_EL) and
5871                $node->[1] ne 'address' and $node->[1] ne 'div') {                not ($node->[1] & ADDRESS_EL) and
5872                  not ($node->[1] & DIV_EL)) {
5873                !!!cp ('t358');
5874              last LI;              last LI;
5875            }            }
5876                        
5877              !!!cp ('t359');
5878            ## Step 4            ## Step 4
5879            $i--;            $i--;
5880            $node = $self->{open_elements}->[$i];            $node = $self->{open_elements}->[$i];
5881            redo LI;            redo LI;
5882          } # LI          } # LI
5883                        
5884          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5885          !!!next-token;          !!!next-token;
5886          redo B;          redo B;
5887        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
5888          ## has a p element in scope          ## has a p element in scope
5889          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
5890            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
5891                !!!cp ('t367');
5892              !!!back-token;              !!!back-token;
5893              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
5894                          line => $token->{line}, column => $token->{column}};
5895              redo B;              redo B;
5896            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
5897                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t368');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
5898              last INSCOPE;              last INSCOPE;
5899            }            }
5900          } # INSCOPE          } # INSCOPE
5901                        
5902          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5903                        
5904          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
5905                        
5906          !!!next-token;          !!!next-token;
5907          redo B;          redo B;
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         redo B;  
5908        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
5909          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
5910            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
5911            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
5912              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
5913                !!!parse-error (type => 'in a:a', token => $token);
5914                            
5915              !!!back-token;              !!!back-token;
5916              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
5917              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
5918                $formatting_end_tag->($token);
5919                            
5920              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
5921                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
5922                    !!!cp ('t372');
5923                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
5924                  last AFE2;                  last AFE2;
5925                }                }
5926              } # AFE2              } # AFE2
5927              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
5928                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
5929                    !!!cp ('t373');
5930                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
5931                  last OE;                  last OE;
5932                }                }
5933              } # OE              } # OE
5934              last AFE;              last AFE;
5935            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
5936                !!!cp ('t374');
5937              last AFE;              last AFE;
5938            }            }
5939          } # AFE          } # AFE
5940                        
5941          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
5942    
5943          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5944          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
5945    
5946          !!!next-token;          !!!next-token;
5947          redo B;          redo B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         redo B;  
5948        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
5949          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
5950    
5951          ## has a |nobr| element in scope          ## has a |nobr| element in scope
5952          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5953            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5954            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
5955              !!!parse-error (type => 'in nobr:nobr');              !!!cp ('t376');
5956                !!!parse-error (type => 'in nobr:nobr', token => $token);
5957              !!!back-token;              !!!back-token;
5958              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
5959                          line => $token->{line}, column => $token->{column}};
5960              redo B;              redo B;
5961            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
5962                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t377');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
5963              last INSCOPE;              last INSCOPE;
5964            }            }
5965          } # INSCOPE          } # INSCOPE
5966                    
5967          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5968          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
5969                    
5970          !!!next-token;          !!!next-token;
# Line 4984  sub _tree_construction_main ($) { Line 5973  sub _tree_construction_main ($) {
5973          ## has a button element in scope          ## has a button element in scope
5974          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5975            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5976            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
5977              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
5978                !!!parse-error (type => 'in button:button', token => $token);
5979              !!!back-token;              !!!back-token;
5980              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
5981                          line => $token->{line}, column => $token->{column}};
5982              redo B;              redo B;
5983            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
5984                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t379');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
5985              last INSCOPE;              last INSCOPE;
5986            }            }
5987          } # INSCOPE          } # INSCOPE
5988                        
5989          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
5990                        
5991          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5992          push @$active_formatting_elements, ['#marker', ''];  
5993            ## TODO: associate with $self->{form_element} if defined
5994    
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
5995          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
5996            
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
5997          !!!next-token;          !!!next-token;
5998          redo B;          redo B;
5999        } elsif ({        } elsif ({
6000                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
6001                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
6002                  image => 1,                  noembed => 1,
6003                    noframes => 1,
6004                    noscript => 0, ## TODO: 1 if scripting is enabled
6005                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6006          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
6007            !!!parse-error (type => 'image');            !!!cp ('t381');
6008            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
6009            } else {
6010              !!!cp ('t399');
6011          }          }
6012            ## NOTE: There is an "as if in body" code clone.
6013          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
6014          redo B;          redo B;
6015        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
6016          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
6017                    
6018          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
6019              !!!cp ('t389');
6020            ## Ignore the token            ## Ignore the token
6021            !!!next-token;            !!!next-token;
6022            redo B;            redo B;
# Line 5102  sub _tree_construction_main ($) { Line 6030  sub _tree_construction_main ($) {
6030            delete $at->{prompt};            delete $at->{prompt};
6031            my @tokens = (            my @tokens = (
6032                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
6033                           attributes => $form_attrs},                           attributes => $form_attrs,
6034                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
6035                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
6036                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
6037                            {type => START_TAG_TOKEN, tag_name => 'p',
6038                             line => $token->{line}, column => $token->{column}},
6039                            {type => START_TAG_TOKEN, tag_name => 'label',
6040                             line => $token->{line}, column => $token->{column}},
6041                         );                         );
6042            if ($prompt_attr) {            if ($prompt_attr) {
6043              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
6044                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
6045                               #line => $token->{line}, column => $token->{column},
6046                              };
6047            } else {            } else {
6048                !!!cp ('t391');
6049              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
6050                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
6051                               #line => $token->{line}, column => $token->{column},
6052                              }; # SHOULD
6053              ## TODO: make this configurable              ## TODO: make this configurable
6054            }            }
6055            push @tokens,            push @tokens,
6056                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
6057                             line => $token->{line}, column => $token->{column}},
6058                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
6059                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
6060                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
6061                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
6062                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
6063                            {type => START_TAG_TOKEN, tag_name => 'hr',
6064                             line => $token->{line}, column => $token->{column}},
6065                            {type => END_TAG_TOKEN, tag_name => 'form',
6066                             line => $token->{line}, column => $token->{column}};
6067            $token = shift @tokens;            $token = shift @tokens;
6068            !!!back-token (@tokens);            !!!back-token (@tokens);
6069            redo B;            redo B;
# Line 5128  sub _tree_construction_main ($) { Line 6071  sub _tree_construction_main ($) {
6071        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
6072          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
6073          my $el;          my $el;
6074          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);
6075                    
6076          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
6077          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5141  sub _tree_construction_main ($) { Line 6084  sub _tree_construction_main ($) {
6084          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
6085            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
6086            unless (length $token->{data}) {            unless (length $token->{data}) {
6087                !!!cp ('t392');
6088              !!!next-token;              !!!next-token;
6089              } else {
6090                !!!cp ('t393');
6091            }            }
6092            } else {
6093              !!!cp ('t394');
6094          }          }
6095          while ($token->{type} == CHARACTER_TOKEN) {          while ($token->{type} == CHARACTER_TOKEN) {
6096              !!!cp ('t395');
6097            $text .= $token->{data};            $text .= $token->{data};
6098            !!!next-token;            !!!next-token;
6099          }          }
6100          if (length $text) {          if (length $text) {
6101              !!!cp ('t396');
6102            $el->manakai_append_text ($text);            $el->manakai_append_text ($text);
6103          }          }
6104                    
# Line 5156  sub _tree_construction_main ($) { Line 6106  sub _tree_construction_main ($) {
6106                    
6107          if ($token->{type} == END_TAG_TOKEN and          if ($token->{type} == END_TAG_TOKEN and
6108              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
6109              !!!cp ('t397');
6110            ## Ignore the token            ## Ignore the token
6111          } else {          } else {
6112            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!cp ('t398');
6113              !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
6114          }          }
6115          !!!next-token;          !!!next-token;
6116          redo B;          redo B;
6117        } elsif ({        } elsif ({
                 iframe => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         ## NOTE: There is an "as if in body" code clone.  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = IN_SELECT_IM;  
         !!!next-token;  
         redo B;  
       } elsif ({  
6118                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
6119                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
6120                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
6121                  thead => 1, tr => 1,                  thead => 1, tr => 1,
6122                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6123          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
6124            !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);
6125          ## Ignore the token          ## Ignore the token
6126          !!!next-token;          !!!next-token;
6127          redo B;          redo B;
6128                    
6129          ## ISSUE: An issue on HTML5 new elements in the spec.          ## ISSUE: An issue on HTML5 new elements in the spec.
6130        } else {        } else {
6131            if ($token->{tag_name} eq 'image') {
6132              !!!cp ('t384');
6133              !!!parse-error (type => 'image', token => $token);
6134              $token->{tag_name} = 'img';
6135            } else {
6136              !!!cp ('t385');
6137            }
6138    
6139            ## NOTE: There is an "as if <br>" code clone.
6140          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6141                    
6142          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6143    
6144            if ({
6145                 applet => 1, marquee => 1, object => 1,
6146                }->{$token->{tag_name}}) {
6147              !!!cp ('t380');
6148              push @$active_formatting_elements, ['#marker', ''];
6149            } elsif ({
6150                      b => 1, big => 1, em => 1, font => 1, i => 1,
6151                      s => 1, small => 1, strile => 1,
6152                      strong => 1, tt => 1, u => 1,
6153                     }->{$token->{tag_name}}) {
6154              !!!cp ('t375');
6155              push @$active_formatting_elements, $self->{open_elements}->[-1];
6156            } elsif ($token->{tag_name} eq 'input') {
6157              !!!cp ('t388');
6158              ## TODO: associate with $self->{form_element} if defined
6159              pop @{$self->{open_elements}};
6160            } elsif ({
6161                      area => 1, basefont => 1, bgsound => 1, br => 1,
6162                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
6163                      #image => 1,
6164                     }->{$token->{tag_name}}) {
6165              !!!cp ('t388.1');
6166              pop @{$self->{open_elements}};
6167            } elsif ($token->{tag_name} eq 'select') {
6168              ## TODO: associate with $self->{form_element} if defined
6169            
6170              if ($self->{insertion_mode} & TABLE_IMS or
6171                  $self->{insertion_mode} & BODY_TABLE_IMS or
6172                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6173                !!!cp ('t400.1');
6174                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
6175              } else {
6176                !!!cp ('t400.2');
6177                $self->{insertion_mode} = IN_SELECT_IM;
6178              }
6179            } else {
6180              !!!cp ('t402');
6181            }
6182                    
6183          !!!next-token;          !!!next-token;
6184          redo B;          redo B;
6185        }        }
6186      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
6187        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
6188          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
6189              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
6190            for (@{$self->{open_elements}}) {          INSCOPE: {
6191              unless ({            for (reverse @{$self->{open_elements}}) {
6192                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
6193                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
6194                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
6195                      }->{$_->[1]}) {                last INSCOPE;
6196                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
6197                  !!!cp ('t405.1');
6198                  last;
6199              }              }
6200            }            }
6201    
6202            $self->{insertion_mode} = AFTER_BODY_IM;            !!!parse-error (type => 'start tag not allowed',
6203            !!!next-token;                            value => $token->{tag_name}, token => $token);
6204            redo B;            ## NOTE: Ignore the token.
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
6205            !!!next-token;            !!!next-token;
6206            redo B;            redo B;
6207            } # INSCOPE
6208    
6209            for (@{$self->{open_elements}}) {
6210              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
6211                !!!cp ('t403');
6212                !!!parse-error (type => 'not closed',
6213                                value => $_->[0]->manakai_local_name,
6214                                token => $token);
6215                last;
6216              } else {
6217                !!!cp ('t404');
6218              }
6219          }          }
6220    
6221            $self->{insertion_mode} = AFTER_BODY_IM;
6222            !!!next-token;
6223            redo B;
6224        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
6225          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
6226            ## up-to-date, though it has same effect as speced.
6227            if (@{$self->{open_elements}} > 1 and
6228                $self->{open_elements}->[1]->[1] & BODY_EL) {
6229            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
6230            if ($self->{open_elements}->[-1]->[1] ne 'body') {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
6231              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!cp ('t406');
6232                !!!parse-error (type => 'not closed',
6233                                value => $self->{open_elements}->[1]->[0]
6234                                    ->manakai_local_name,
6235                                token => $token);
6236              } else {
6237                !!!cp ('t407');
6238            }            }
6239            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
6240            ## reprocess            ## reprocess
6241            redo B;            redo B;
6242          } else {          } else {
6243            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
6244              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6245            ## Ignore the token            ## Ignore the token
6246            !!!next-token;            !!!next-token;
6247            redo B;            redo B;
# Line 5241  sub _tree_construction_main ($) { Line 6250  sub _tree_construction_main ($) {
6250                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
6251                  div => 1, dl => 1, fieldset => 1, listing => 1,                  div => 1, dl => 1, fieldset => 1, listing => 1,
6252                  menu => 1, ol => 1, pre => 1, ul => 1,                  menu => 1, ol => 1, pre => 1, ul => 1,
                 p => 1,  
6253                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
6254                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
6255                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6256          ## has an element in scope          ## has an element in scope
6257          my $i;          my $i;
6258          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6259            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6260            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6261              ## generate implied end tags              !!!cp ('t410');
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
6262              $i = $_;              $i = $_;
6263              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
6264            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
6265                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6266              last INSCOPE;              last INSCOPE;
6267            }            }
6268          } # INSCOPE          } # INSCOPE
6269            
6270          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
6271            if (defined $i) {            !!!cp ('t413');
6272              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6273            } else {
6274              ## Step 1. generate implied end tags
6275              while ({
6276                      dd => ($token->{tag_name} ne 'dd'),
6277                      dt => ($token->{tag_name} ne 'dt'),
6278                      li => ($token->{tag_name} ne 'li'),
6279                      p => 1,
6280                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
6281                !!!cp ('t409');
6282                pop @{$self->{open_elements}};
6283              }
6284    
6285              ## Step 2.
6286              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6287                      ne $token->{tag_name}) {
6288                !!!cp ('t412');
6289                !!!parse-error (type => 'not closed',
6290                                value => $self->{open_elements}->[-1]->[0]
6291                                    ->manakai_local_name,
6292                                token => $token);
6293            } else {            } else {
6294              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
6295            }            }
6296          }  
6297                      ## Step 3.
         if (defined $i) {  
6298            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6299          } elsif ($token->{tag_name} eq 'p') {  
6300            ## As if <p>, then reprocess the current token            ## Step 4.
6301            my $el;            $clear_up_to_marker->()
6302            !!!create-element ($el, 'p');                if {
6303            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
6304                  }->{$token->{tag_name}};
6305          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
6306          !!!next-token;          !!!next-token;
6307          redo B;          redo B;
6308        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
6309            undef $self->{form_element};
6310    
6311          ## has an element in scope          ## has an element in scope
6312            my $i;
6313          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6314            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6315            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
6316              ## generate implied end tags              !!!cp ('t418');
6317              if ({              $i = $_;
                  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_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
6318              last INSCOPE;              last INSCOPE;
6319            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
6320                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6321              last INSCOPE;              last INSCOPE;
6322            }            }
6323          } # INSCOPE          } # INSCOPE
6324            
6325          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
6326            pop @{$self->{open_elements}};            !!!cp ('t421');
6327          } else {            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6328            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } else {
6329              ## Step 1. generate implied end tags
6330              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6331                !!!cp ('t417');
6332                pop @{$self->{open_elements}};
6333              }
6334              
6335              ## Step 2.
6336              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6337                      ne $token->{tag_name}) {
6338                !!!cp ('t417.1');
6339                !!!parse-error (type => 'not closed',
6340                                value => $self->{open_elements}->[-1]->[0]
6341                                    ->manakai_local_name,
6342                                token => $token);
6343              } else {
6344                !!!cp ('t420');
6345              }  
6346              
6347              ## Step 3.
6348              splice @{$self->{open_elements}}, $i;
6349          }          }
6350    
         undef $self->{form_element};  
6351          !!!next-token;          !!!next-token;
6352          redo B;          redo B;
6353        } elsif ({        } elsif ({
# Line 5337  sub _tree_construction_main ($) { Line 6357  sub _tree_construction_main ($) {
6357          my $i;          my $i;
6358          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6359            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6360            if ({            if ($node->[1] & HEADING_EL) {
6361                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,              !!!cp ('t423');
               }->{$node->[1]}) {  
             ## 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_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
6362              $i = $_;              $i = $_;
6363              last INSCOPE;              last INSCOPE;
6364            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
6365                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6366              last INSCOPE;              last INSCOPE;
6367            }            }
6368          } # INSCOPE          } # INSCOPE
6369            
6370          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
6371            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t425.1');
6372              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6373            } else {
6374              ## Step 1. generate implied end tags
6375              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6376                !!!cp ('t422');
6377                pop @{$self->{open_elements}};
6378              }
6379              
6380              ## Step 2.
6381              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6382                      ne $token->{tag_name}) {
6383                !!!cp ('t425');
6384                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6385              } else {
6386                !!!cp ('t426');
6387              }
6388    
6389              ## Step 3.
6390              splice @{$self->{open_elements}}, $i;
6391          }          }
6392                    
6393          splice @{$self->{open_elements}}, $i if defined $i;          !!!next-token;
6394            redo B;
6395          } elsif ($token->{tag_name} eq 'p') {
6396            ## has an element in scope
6397            my $i;
6398            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6399              my $node = $self->{open_elements}->[$_];
6400              if ($node->[1] & P_EL) {
6401                !!!cp ('t410.1');
6402                $i = $_;
6403                last INSCOPE;
6404              } elsif ($node->[1] & SCOPING_EL) {
6405                !!!cp ('t411.1');
6406                last INSCOPE;
6407              }
6408            } # INSCOPE
6409    
6410            if (defined $i) {
6411              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6412                      ne $token->{tag_name}) {
6413                !!!cp ('t412.1');
6414                !!!parse-error (type => 'not closed',
6415                                value => $self->{open_elements}->[-1]->[0]
6416                                    ->manakai_local_name,
6417                                token => $token);
6418              } else {
6419                !!!cp ('t414.1');
6420              }
6421    
6422              splice @{$self->{open_elements}}, $i;
6423            } else {
6424              !!!cp ('t413.1');
6425              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6426    
6427              !!!cp ('t415.1');
6428              ## As if <p>, then reprocess the current token
6429              my $el;
6430              !!!create-element ($el, 'p',, $token);
6431              $insert->($el);
6432              ## NOTE: Not inserted into |$self->{open_elements}|.
6433            }
6434    
6435          !!!next-token;          !!!next-token;
6436          redo B;          redo B;
6437        } elsif ({        } elsif ({
# Line 5374  sub _tree_construction_main ($) { Line 6440  sub _tree_construction_main ($) {
6440                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strile => 1,
6441                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
6442                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6443          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
6444            $formatting_end_tag->($token);
6445          redo B;          redo B;
6446        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
6447          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
6448            !!!parse-error (type => 'unmatched end tag:br', token => $token);
6449    
6450          ## As if <br>          ## As if <br>
6451          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6452                    
6453          my $el;          my $el;
6454          !!!create-element ($el, 'br');          !!!create-element ($el, 'br',, $token);
6455          $insert->($el);          $insert->($el);
6456                    
6457          ## Ignore the token.          ## Ignore the token.
# Line 5401  sub _tree_construction_main ($) { Line 6469  sub _tree_construction_main ($) {
6469                  table => 1, textarea => 1, wbr => 1,                  table => 1, textarea => 1, wbr => 1,
6470                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
6471                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6472          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t429');
6473            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6474          ## Ignore the token          ## Ignore the token
6475          !!!next-token;          !!!next-token;
6476          redo B;          redo B;
# Line 5415  sub _tree_construction_main ($) { Line 6484  sub _tree_construction_main ($) {
6484    
6485          ## Step 2          ## Step 2
6486          S2: {          S2: {
6487            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6488              ## Step 1              ## Step 1
6489              ## generate implied end tags              ## generate implied end tags
6490              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6491                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
6492                   td => 1, th => 1, tr => 1,                ## ISSUE: Can this case be reached?
6493                   tbody => 1, tfoot => 1, thead => 1,                pop @{$self->{open_elements}};
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
6494              }              }
6495                    
6496              ## Step 2              ## Step 2
6497              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6498                        ne $token->{tag_name}) {
6499                  !!!cp ('t431');
6500                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
6501                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
6502                                  value => $self->{open_elements}->[-1]->[0]
6503                                      ->manakai_local_name,
6504                                  token => $token);
6505                } else {
6506                  !!!cp ('t432');
6507              }              }
6508                            
6509              ## Step 3              ## Step 3
# Line 5442  sub _tree_construction_main ($) { Line 6513  sub _tree_construction_main ($) {
6513              last S2;              last S2;
6514            } else {            } else {
6515              ## Step 3              ## Step 3
6516              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
6517                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
6518                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
6519                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
6520                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
6521                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6522                ## Ignore the token                ## Ignore the token
6523                !!!next-token;                !!!next-token;
6524                last S2;                last S2;
6525              }              }
6526    
6527                !!!cp ('t434');
6528            }            }
6529                        
6530            ## Step 4            ## Step 4
# Line 5466  sub _tree_construction_main ($) { Line 6540  sub _tree_construction_main ($) {
6540      redo B;      redo B;
6541    } # B    } # B
6542    
   ## NOTE: The "trailing end" phase in HTML5 is split into  
   ## two insertion modes: "after html body" and "after html frameset".  
   ## NOTE: States in the main stage is preserved while  
   ## the parser stays in the trailing end phase. # MUST  
   
6543    ## Stop parsing # MUST    ## Stop parsing # MUST
6544        
6545    ## TODO: script stuffs    ## TODO: script stuffs
# Line 5512  sub set_inner_html ($$$) { Line 6581  sub set_inner_html ($$$) {
6581      my $p = $class->new;      my $p = $class->new;
6582      $p->{document} = $doc;      $p->{document} = $doc;
6583    
6584      ## Step 9 # MUST      ## Step 8 # MUST
6585      my $i = 0;      my $i = 0;
6586      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
6587      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
6588      $p->{set_next_char} = sub {      $p->{set_next_char} = sub {
6589        my $self = shift;        my $self = shift;
6590    
# Line 5524  sub set_inner_html ($$$) { Line 6593  sub set_inner_html ($$$) {
6593    
6594        $self->{next_char} = -1 and return if $i >= length $$s;        $self->{next_char} = -1 and return if $i >= length $$s;
6595        $self->{next_char} = ord substr $$s, $i++, 1;        $self->{next_char} = ord substr $$s, $i++, 1;
6596        $column++;  
6597          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
6598          $p->{column}++;
6599    
6600        if ($self->{next_char} == 0x000A) { # LF        if ($self->{next_char} == 0x000A) { # LF
6601          $line++;          $p->{line}++;
6602          $column = 0;          $p->{column} = 0;
6603            !!!cp ('i1');
6604        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{next_char} == 0x000D) { # CR
6605          $i++ if substr ($$s, $i, 1) eq "\x0A";          $i++ if substr ($$s, $i, 1) eq "\x0A";
6606          $self->{next_char} = 0x000A; # LF # MUST          $self->{next_char} = 0x000A; # LF # MUST
6607          $line++;          $p->{line}++;
6608          $column = 0;          $p->{column} = 0;
6609            !!!cp ('i2');
6610        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{next_char} > 0x10FFFF) {
6611          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6612            !!!cp ('i3');
6613        } elsif ($self->{next_char} == 0x0000) { # NULL        } elsif ($self->{next_char} == 0x0000) { # NULL
6614            !!!cp ('i4');
6615          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
6616          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
6617        }        }
# Line 5546  sub set_inner_html ($$$) { Line 6621  sub set_inner_html ($$$) {
6621            
6622      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
6623        my (%opt) = @_;        my (%opt) = @_;
6624        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
6625          my $column = $opt{column};
6626          if (defined $opt{token} and defined $opt{token}->{line}) {
6627            $line = $opt{token}->{line};
6628            $column = $opt{token}->{column};
6629          }
6630          warn "Parse error ($opt{type}) at line $line column $column\n";
6631      };      };
6632      $p->{parse_error} = sub {      $p->{parse_error} = sub {
6633        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
6634      };      };
6635            
6636      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
# Line 5573  sub set_inner_html ($$$) { Line 6654  sub set_inner_html ($$$) {
6654          unless defined $p->{content_model};          unless defined $p->{content_model};
6655          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
6656    
6657      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
6658          ## TODO: Foreign element OK?
6659    
6660      ## Step 4      ## Step 3
6661      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
6662        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
6663    
6664      ## Step 5 # MUST      ## Step 4 # MUST
6665      $doc->append_child ($root);      $doc->append_child ($root);
6666    
6667      ## Step 6 # MUST      ## Step 5 # MUST
6668      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
6669    
6670      undef $p->{head_element};      undef $p->{head_element};
6671    
6672      ## Step 7 # MUST      ## Step 6 # MUST
6673      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
6674    
6675      ## Step 8 # MUST      ## Step 7 # MUST
6676      my $anode = $node;      my $anode = $node;
6677      AN: while (defined $anode) {      AN: while (defined $anode) {
6678        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
6679          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
6680          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
6681            if ($anode->manakai_local_name eq 'form') {            if ($anode->manakai_local_name eq 'form') {
6682                !!!cp ('i5');
6683              $p->{form_element} = $anode;              $p->{form_element} = $anode;
6684              last AN;              last AN;
6685            }            }
# Line 5605  sub set_inner_html ($$$) { Line 6688  sub set_inner_html ($$$) {
6688        $anode = $anode->parent_node;        $anode = $anode->parent_node;
6689      } # AN      } # AN
6690            
6691      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
6692      {      {
6693        my $self = $p;        my $self = $p;
6694        !!!next-token;        !!!next-token;
6695      }      }
6696      $p->_tree_construction_main;      $p->_tree_construction_main;
6697    
6698      ## Step 11 # MUST      ## Step 10 # MUST
6699      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
6700      for (@cn) {      for (@cn) {
6701        $node->remove_child ($_);        $node->remove_child ($_);
6702      }      }
6703      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
6704    
6705      ## Step 12 # MUST      ## Step 11 # MUST
6706      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
6707      for (@cn) {      for (@cn) {
6708        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5629  sub set_inner_html ($$$) { Line 6711  sub set_inner_html ($$$) {
6711      ## ISSUE: mutation events?      ## ISSUE: mutation events?
6712    
6713      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
6714    
6715        delete $p->{parse_error}; # delete loop
6716    } else {    } else {
6717      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
6718    }    }

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24