/[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.86 by wakaba, Thu Mar 6 15:56:52 2008 UTC revision 1.130 by wakaba, Sat Apr 12 15:47:13 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  my $permitted_slash_tag_name = {  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
16    base => 1,  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
17    link => 1,  my $SVG_NS = q<http://www.w3.org/2000/svg>;
18    meta => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
19    hr => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
20    br => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
21    img => 1,  
22    embed => 1,  sub A_EL () { 0b1 }
23    param => 1,  sub ADDRESS_EL () { 0b10 }
24    area => 1,  sub BODY_EL () { 0b100 }
25    col => 1,  sub BUTTON_EL () { 0b1000 }
26    input => 1,  sub CAPTION_EL () { 0b10000 }
27    sub DD_EL () { 0b100000 }
28    sub DIV_EL () { 0b1000000 }
29    sub DT_EL () { 0b10000000 }
30    sub FORM_EL () { 0b100000000 }
31    sub FORMATTING_EL () { 0b1000000000 }
32    sub FRAMESET_EL () { 0b10000000000 }
33    sub HEADING_EL () { 0b100000000000 }
34    sub HTML_EL () { 0b1000000000000 }
35    sub LI_EL () { 0b10000000000000 }
36    sub NOBR_EL () { 0b100000000000000 }
37    sub OPTION_EL () { 0b1000000000000000 }
38    sub OPTGROUP_EL () { 0b10000000000000000 }
39    sub P_EL () { 0b100000000000000000 }
40    sub SELECT_EL () { 0b1000000000000000000 }
41    sub TABLE_EL () { 0b10000000000000000000 }
42    sub TABLE_CELL_EL () { 0b100000000000000000000 }
43    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
44    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
45    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
46    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
47    sub FOREIGN_EL () { 0b10000000000000000000000000 }
48    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
49    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
50    
51    sub TABLE_ROWS_EL () {
52      TABLE_EL |
53      TABLE_ROW_EL |
54      TABLE_ROW_GROUP_EL
55    }
56    
57    sub END_TAG_OPTIONAL_EL () {
58      DD_EL |
59      DT_EL |
60      LI_EL |
61      P_EL
62    }
63    
64    sub ALL_END_TAG_OPTIONAL_EL () {
65      END_TAG_OPTIONAL_EL |
66      BODY_EL |
67      HTML_EL |
68      TABLE_CELL_EL |
69      TABLE_ROW_EL |
70      TABLE_ROW_GROUP_EL
71    }
72    
73    sub SCOPING_EL () {
74      BUTTON_EL |
75      CAPTION_EL |
76      HTML_EL |
77      TABLE_EL |
78      TABLE_CELL_EL |
79      MISC_SCOPING_EL
80    }
81    
82    sub TABLE_SCOPING_EL () {
83      HTML_EL |
84      TABLE_EL
85    }
86    
87    sub TABLE_ROWS_SCOPING_EL () {
88      HTML_EL |
89      TABLE_ROW_GROUP_EL
90    }
91    
92    sub TABLE_ROW_SCOPING_EL () {
93      HTML_EL |
94      TABLE_ROW_EL
95    }
96    
97    sub SPECIAL_EL () {
98      ADDRESS_EL |
99      BODY_EL |
100      DIV_EL |
101      END_TAG_OPTIONAL_EL |
102      FORM_EL |
103      FRAMESET_EL |
104      HEADING_EL |
105      OPTION_EL |
106      OPTGROUP_EL |
107      SELECT_EL |
108      TABLE_ROW_EL |
109      TABLE_ROW_GROUP_EL |
110      MISC_SPECIAL_EL
111    }
112    
113    my $el_category = {
114      a => A_EL | FORMATTING_EL,
115      address => ADDRESS_EL,
116      applet => MISC_SCOPING_EL,
117      area => MISC_SPECIAL_EL,
118      b => FORMATTING_EL,
119      base => MISC_SPECIAL_EL,
120      basefont => MISC_SPECIAL_EL,
121      bgsound => MISC_SPECIAL_EL,
122      big => FORMATTING_EL,
123      blockquote => MISC_SPECIAL_EL,
124      body => BODY_EL,
125      br => MISC_SPECIAL_EL,
126      button => BUTTON_EL,
127      caption => CAPTION_EL,
128      center => MISC_SPECIAL_EL,
129      col => MISC_SPECIAL_EL,
130      colgroup => MISC_SPECIAL_EL,
131      dd => DD_EL,
132      dir => MISC_SPECIAL_EL,
133      div => DIV_EL,
134      dl => MISC_SPECIAL_EL,
135      dt => DT_EL,
136      em => FORMATTING_EL,
137      embed => MISC_SPECIAL_EL,
138      fieldset => MISC_SPECIAL_EL,
139      font => FORMATTING_EL,
140      form => FORM_EL,
141      frame => MISC_SPECIAL_EL,
142      frameset => FRAMESET_EL,
143      h1 => HEADING_EL,
144      h2 => HEADING_EL,
145      h3 => HEADING_EL,
146      h4 => HEADING_EL,
147      h5 => HEADING_EL,
148      h6 => HEADING_EL,
149      head => MISC_SPECIAL_EL,
150      hr => MISC_SPECIAL_EL,
151      html => HTML_EL,
152      i => FORMATTING_EL,
153      iframe => MISC_SPECIAL_EL,
154      img => MISC_SPECIAL_EL,
155      input => MISC_SPECIAL_EL,
156      isindex => MISC_SPECIAL_EL,
157      li => LI_EL,
158      link => MISC_SPECIAL_EL,
159      listing => MISC_SPECIAL_EL,
160      marquee => MISC_SCOPING_EL,
161      menu => MISC_SPECIAL_EL,
162      meta => MISC_SPECIAL_EL,
163      nobr => NOBR_EL | FORMATTING_EL,
164      noembed => MISC_SPECIAL_EL,
165      noframes => MISC_SPECIAL_EL,
166      noscript => MISC_SPECIAL_EL,
167      object => MISC_SCOPING_EL,
168      ol => MISC_SPECIAL_EL,
169      optgroup => OPTGROUP_EL,
170      option => OPTION_EL,
171      p => P_EL,
172      param => MISC_SPECIAL_EL,
173      plaintext => MISC_SPECIAL_EL,
174      pre => MISC_SPECIAL_EL,
175      s => FORMATTING_EL,
176      script => MISC_SPECIAL_EL,
177      select => SELECT_EL,
178      small => FORMATTING_EL,
179      spacer => MISC_SPECIAL_EL,
180      strike => FORMATTING_EL,
181      strong => FORMATTING_EL,
182      style => MISC_SPECIAL_EL,
183      table => TABLE_EL,
184      tbody => TABLE_ROW_GROUP_EL,
185      td => TABLE_CELL_EL,
186      textarea => MISC_SPECIAL_EL,
187      tfoot => TABLE_ROW_GROUP_EL,
188      th => TABLE_CELL_EL,
189      thead => TABLE_ROW_GROUP_EL,
190      title => MISC_SPECIAL_EL,
191      tr => TABLE_ROW_EL,
192      tt => FORMATTING_EL,
193      u => FORMATTING_EL,
194      ul => MISC_SPECIAL_EL,
195      wbr => MISC_SPECIAL_EL,
196    };
197    
198    my $el_category_f = {
199      $MML_NS => {
200        'annotation-xml' => MML_AXML_EL,
201        mi => FOREIGN_FLOW_CONTENT_EL,
202        mo => FOREIGN_FLOW_CONTENT_EL,
203        mn => FOREIGN_FLOW_CONTENT_EL,
204        ms => FOREIGN_FLOW_CONTENT_EL,
205        mtext => FOREIGN_FLOW_CONTENT_EL,
206      },
207      $SVG_NS => {
208        foreignobject => FOREIGN_FLOW_CONTENT_EL, ## TODO: case
209        desc => FOREIGN_FLOW_CONTENT_EL,
210        title => FOREIGN_FLOW_CONTENT_EL,
211      },
212      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
213  };  };
214    
215  my $c1_entity_char = {  my $c1_entity_char = {
# Line 61  my $c1_entity_char = { Line 247  my $c1_entity_char = {
247    0x9F => 0x0178,    0x9F => 0x0178,
248  }; # $c1_entity_char  }; # $c1_entity_char
249    
 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  
   
250  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
251    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
252    my $charset = shift;    my $charset = shift;
# Line 108  sub parse_byte_string ($$$$;$) { Line 272  sub parse_byte_string ($$$$;$) {
272    $self->{change_encoding} = sub {    $self->{change_encoding} = sub {
273      my $self = shift;      my $self = shift;
274      my $charset = lc shift;      my $charset = lc shift;
275        my $token = shift;
276      ## TODO: if $charset is supported      ## TODO: if $charset is supported
277      ## TODO: normalize charset name      ## TODO: normalize charset name
278    
# Line 126  sub parse_byte_string ($$$$;$) { Line 291  sub parse_byte_string ($$$$;$) {
291      }      }
292    
293      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.
294          ':'.$charset, level => 'w');          ':'.$charset, level => 'w', token => $token);
295    
296      ## Step 3      ## Step 3
297      # if (can) {      # if (can) {
# Line 177  sub parse_string ($$$;$) { Line 342  sub parse_string ($$$;$) {
342        if defined $self->{input_encoding};        if defined $self->{input_encoding};
343    
344    my $i = 0;    my $i = 0;
345    my $line = 1;    $self->{line_prev} = $self->{line} = 1;
346    my $column = 0;    $self->{column_prev} = $self->{column} = 0;
347    $self->{set_next_char} = sub {    $self->{set_next_char} = sub {
348      my $self = shift;      my $self = shift;
349    
# Line 187  sub parse_string ($$$;$) { Line 352  sub parse_string ($$$;$) {
352    
353      $self->{next_char} = -1 and return if $i >= length $$s;      $self->{next_char} = -1 and return if $i >= length $$s;
354      $self->{next_char} = ord substr $$s, $i++, 1;      $self->{next_char} = ord substr $$s, $i++, 1;
355      $column++;  
356        ($self->{line_prev}, $self->{column_prev})
357            = ($self->{line}, $self->{column});
358        $self->{column}++;
359            
360      if ($self->{next_char} == 0x000A) { # LF      if ($self->{next_char} == 0x000A) { # LF
361        $line++;        $self->{line}++;
362        $column = 0;        $self->{column} = 0;
363      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{next_char} == 0x000D) { # CR
364        $i++ if substr ($$s, $i, 1) eq "\x0A";        $i++ if substr ($$s, $i, 1) eq "\x0A";
365        $self->{next_char} = 0x000A; # LF # MUST        $self->{next_char} = 0x000A; # LF # MUST
366        $line++;        $self->{line}++;
367        $column = 0;        $self->{column} = 0;
368      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{next_char} > 0x10FFFF) {
369        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
370      } elsif ($self->{next_char} == 0x0000) { # NULL      } elsif ($self->{next_char} == 0x0000) { # NULL
# Line 209  sub parse_string ($$$;$) { Line 377  sub parse_string ($$$;$) {
377    
378    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
379      my (%opt) = @_;      my (%opt) = @_;
380      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
381        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
382        warn "Parse error ($opt{type}) at line $line column $column\n";
383    };    };
384    $self->{parse_error} = sub {    $self->{parse_error} = sub {
385      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
386    };    };
387    
388    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 220  sub parse_string ($$$;$) { Line 390  sub parse_string ($$$;$) {
390    $self->_construct_tree;    $self->_construct_tree;
391    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
392    
393      delete $self->{parse_error}; # remove loop
394    
395    return $self->{document};    return $self->{document};
396  } # parse_string  } # parse_string
397    
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 459  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
459  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
460  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
461  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
462    sub SELF_CLOSING_START_TAG_STATE () { 34 }
463    sub CDATA_BLOCK_STATE () { 35 }
464    
465  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
466  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 477  sub TABLE_IMS ()      { 0b1000000 }
477  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
478  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
479  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
480    sub SELECT_IMS ()     { 0b10000000000 }
481    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
482        ## NOTE: "in foreign content" insertion mode is special; it is combined
483        ## with the secondary insertion mode.  In this parser, they are stored
484        ## together in the bit-or'ed form.
485    
486  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
487    
# Line 325  sub IN_TABLE_IM () { TABLE_IMS } Line 504  sub IN_TABLE_IM () { TABLE_IMS }
504  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
505  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
506  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
507  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
508    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
509  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
510    
511  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 338  sub _initialize_tokenizer ($) { Line 518  sub _initialize_tokenizer ($) {
518    undef $self->{current_attribute};    undef $self->{current_attribute};
519    undef $self->{last_emitted_start_tag_name};    undef $self->{last_emitted_start_tag_name};
520    undef $self->{last_attribute_value_state};    undef $self->{last_attribute_value_state};
521      delete $self->{self_closing};
522    $self->{char} = [];    $self->{char} = [];
523    # $self->{next_char}    # $self->{next_char}
524    !!!next-input-character;    !!!next-input-character;
# Line 358  sub _initialize_tokenizer ($) { Line 539  sub _initialize_tokenizer ($) {
539  ##        ->{value}  ##        ->{value}
540  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
541  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
542    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
543    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
544    ##     while the token is pushed back to the stack.
545    
546    ## ISSUE: "When a DOCTYPE token is created, its
547    ## <i>self-closing flag</i> must be unset (its other state is that it
548    ## be set), and its attributes list must be empty.": Wrong subject?
549    
550  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
551    
# Line 384  sub _initialize_tokenizer ($) { Line 572  sub _initialize_tokenizer ($) {
572    
573  sub _get_next_token ($) {  sub _get_next_token ($) {
574    my $self = shift;    my $self = shift;
575    
576      if ($self->{self_closing}) {
577        !!!parse-error (type => 'nestc', token => $self->{current_token});
578        ## NOTE: The |self_closing| flag is only set by start tag token.
579        ## In addition, when a start tag token is emitted, it is always set to
580        ## |current_token|.
581        delete $self->{self_closing};
582      }
583    
584    if (@{$self->{token}}) {    if (@{$self->{token}}) {
585        $self->{self_closing} = $self->{token}->[0]->{self_closing};
586      return shift @{$self->{token}};      return shift @{$self->{token}};
587    }    }
588    
# Line 447  sub _get_next_token ($) { Line 645  sub _get_next_token ($) {
645          #          #
646        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
647          !!!cp (11);          !!!cp (11);
648          !!!emit ({type => END_OF_FILE_TOKEN});          !!!emit ({type => END_OF_FILE_TOKEN,
649                      line => $self->{line}, column => $self->{column}});
650          last A; ## TODO: ok?          last A; ## TODO: ok?
651        } else {        } else {
652          !!!cp (12);          !!!cp (12);
653        }        }
654        # Anything else        # Anything else
655        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
656                     data => chr $self->{next_char}};                     data => chr $self->{next_char},
657                       line => $self->{line}, column => $self->{column},
658                      };
659        ## Stay in the data state        ## Stay in the data state
660        !!!next-input-character;        !!!next-input-character;
661    
# Line 463  sub _get_next_token ($) { Line 664  sub _get_next_token ($) {
664        redo A;        redo A;
665      } elsif ($self->{state} == ENTITY_DATA_STATE) {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
666        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
667    
668          my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
669                
670        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);
671    
# Line 471  sub _get_next_token ($) { Line 674  sub _get_next_token ($) {
674    
675        unless (defined $token) {        unless (defined $token) {
676          !!!cp (13);          !!!cp (13);
677          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          !!!emit ({type => CHARACTER_TOKEN, data => '&',
678                      line => $l, column => $c,
679                     });
680        } else {        } else {
681          !!!cp (14);          !!!cp (14);
682          !!!emit ($token);          !!!emit ($token);
# Line 490  sub _get_next_token ($) { Line 695  sub _get_next_token ($) {
695            ## reconsume            ## reconsume
696            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
697    
698            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
699                        line => $self->{line_prev},
700                        column => $self->{column_prev},
701                       });
702    
703            redo A;            redo A;
704          }          }
# Line 510  sub _get_next_token ($) { Line 718  sub _get_next_token ($) {
718            !!!cp (19);            !!!cp (19);
719            $self->{current_token}            $self->{current_token}
720              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
721                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{next_char} + 0x0020),
722                   line => $self->{line_prev},
723                   column => $self->{column_prev}};
724            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
725            !!!next-input-character;            !!!next-input-character;
726            redo A;            redo A;
# Line 518  sub _get_next_token ($) { Line 728  sub _get_next_token ($) {
728                   $self->{next_char} <= 0x007A) { # a..z                   $self->{next_char} <= 0x007A) { # a..z
729            !!!cp (20);            !!!cp (20);
730            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{current_token} = {type => START_TAG_TOKEN,
731                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{next_char}),
732                                        line => $self->{line_prev},
733                                        column => $self->{column_prev}};
734            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
735            !!!next-input-character;            !!!next-input-character;
736            redo A;            redo A;
737          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{next_char} == 0x003E) { # >
738            !!!cp (21);            !!!cp (21);
739            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
740                              line => $self->{line_prev},
741                              column => $self->{column_prev});
742            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
743            !!!next-input-character;            !!!next-input-character;
744    
745            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
746                        line => $self->{line_prev},
747                        column => $self->{column_prev},
748                       });
749    
750            redo A;            redo A;
751          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{next_char} == 0x003F) { # ?
752            !!!cp (22);            !!!cp (22);
753            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
754                              line => $self->{line_prev},
755                              column => $self->{column_prev});
756            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
757              $self->{current_token} = {type => COMMENT_TOKEN, data => '',
758                                        line => $self->{line_prev},
759                                        column => $self->{column_prev},
760                                       };
761            ## $self->{next_char} is intentionally left as is            ## $self->{next_char} is intentionally left as is
762            redo A;            redo A;
763          } else {          } else {
# Line 543  sub _get_next_token ($) { Line 766  sub _get_next_token ($) {
766            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
767            ## reconsume            ## reconsume
768    
769            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
770                        line => $self->{line_prev},
771                        column => $self->{column_prev},
772                       });
773    
774            redo A;            redo A;
775          }          }
# Line 551  sub _get_next_token ($) { Line 777  sub _get_next_token ($) {
777          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
778        }        }
779      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
780          my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
781        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
782          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_emitted_start_tag_name}) {
783    
784            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
785            my @next_char;            my @next_char;
786            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 569  sub _get_next_token ($) { Line 797  sub _get_next_token ($) {
797                !!!back-next-input-character (@next_char);                !!!back-next-input-character (@next_char);
798                $self->{state} = DATA_STATE;                $self->{state} = DATA_STATE;
799    
800                !!!emit ({type => CHARACTER_TOKEN, data => '</'});                !!!emit ({type => CHARACTER_TOKEN, data => '</',
801                            line => $l, column => $c,
802                           });
803        
804                redo A;                redo A;
805              }              }
# Line 588  sub _get_next_token ($) { Line 818  sub _get_next_token ($) {
818              $self->{next_char} = shift @next_char; # reconsume              $self->{next_char} = shift @next_char; # reconsume
819              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
820              $self->{state} = DATA_STATE;              $self->{state} = DATA_STATE;
821              !!!emit ({type => CHARACTER_TOKEN, data => '</'});              !!!emit ({type => CHARACTER_TOKEN, data => '</',
822                          line => $l, column => $c,
823                         });
824              redo A;              redo A;
825            } else {            } else {
826              !!!cp (27);              !!!cp (27);
# Line 601  sub _get_next_token ($) { Line 833  sub _get_next_token ($) {
833            !!!cp (28);            !!!cp (28);
834            # next-input-character is already done            # next-input-character is already done
835            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
836            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            !!!emit ({type => CHARACTER_TOKEN, data => '</',
837                        line => $l, column => $c,
838                       });
839            redo A;            redo A;
840          }          }
841        }        }
# Line 609  sub _get_next_token ($) { Line 843  sub _get_next_token ($) {
843        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{next_char} and
844            $self->{next_char} <= 0x005A) { # A..Z            $self->{next_char} <= 0x005A) { # A..Z
845          !!!cp (29);          !!!cp (29);
846          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{current_token}
847                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
848                   tag_name => chr ($self->{next_char} + 0x0020),
849                   line => $l, column => $c};
850          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
851          !!!next-input-character;          !!!next-input-character;
852          redo A;          redo A;
# Line 618  sub _get_next_token ($) { Line 854  sub _get_next_token ($) {
854                 $self->{next_char} <= 0x007A) { # a..z                 $self->{next_char} <= 0x007A) { # a..z
855          !!!cp (30);          !!!cp (30);
856          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{current_token} = {type => END_TAG_TOKEN,
857                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{next_char}),
858                                      line => $l, column => $c};
859          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
860          !!!next-input-character;          !!!next-input-character;
861          redo A;          redo A;
862        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
863          !!!cp (31);          !!!cp (31);
864          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
865                            line => $self->{line_prev}, ## "<" in "</>"
866                            column => $self->{column_prev} - 1);
867          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
868          !!!next-input-character;          !!!next-input-character;
869          redo A;          redo A;
# Line 634  sub _get_next_token ($) { Line 873  sub _get_next_token ($) {
873          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
874          # reconsume          # reconsume
875    
876          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
877                      line => $l, column => $c,
878                     });
879    
880          redo A;          redo A;
881        } else {        } else {
882          !!!cp (33);          !!!cp (33);
883          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
884          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
885            $self->{current_token} = {type => COMMENT_TOKEN, data => '',
886                                      line => $self->{line_prev}, # "<" of "</"
887                                      column => $self->{column_prev} - 1,
888                                     };
889          ## $self->{next_char} is intentionally left as is          ## $self->{next_char} is intentionally left as is
890          redo A;          redo A;
891        }        }
# Line 657  sub _get_next_token ($) { Line 902  sub _get_next_token ($) {
902        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
903          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
904            !!!cp (35);            !!!cp (35);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
905            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
906          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
907            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 690  sub _get_next_token ($) { Line 933  sub _get_next_token ($) {
933          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
934          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
935            !!!cp (39);            !!!cp (39);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
936            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
937          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
938            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 712  sub _get_next_token ($) { Line 953  sub _get_next_token ($) {
953    
954          redo A;          redo A;
955        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
956            !!!cp (42);
957            $self->{state} = SELF_CLOSING_START_TAG_STATE;
958          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (42);  
           #  
         } else {  
           !!!cp (43);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
959          redo A;          redo A;
960        } else {        } else {
961          !!!cp (44);          !!!cp (44);
# Line 747  sub _get_next_token ($) { Line 978  sub _get_next_token ($) {
978        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
979          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
980            !!!cp (46);            !!!cp (46);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
981            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
982          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
983            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 770  sub _get_next_token ($) { Line 999  sub _get_next_token ($) {
999        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
1000                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1001          !!!cp (49);          !!!cp (49);
1002          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{current_attribute}
1003                                value => ''};              = {name => chr ($self->{next_char} + 0x0020),
1004                   value => '',
1005                   line => $self->{line}, column => $self->{column}};
1006          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1007          !!!next-input-character;          !!!next-input-character;
1008          redo A;          redo A;
1009        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1010            !!!cp (50);
1011            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1012          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (50);  
           #  
         } else {  
           !!!cp (51);  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1013          redo A;          redo A;
1014        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1015          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1016          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1017            !!!cp (52);            !!!cp (52);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1018            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1019          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1020            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 825  sub _get_next_token ($) { Line 1044  sub _get_next_token ($) {
1044          } else {          } else {
1045            !!!cp (56);            !!!cp (56);
1046          }          }
1047          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{current_attribute}
1048                                value => ''};              = {name => chr ($self->{next_char}),
1049                   value => '',
1050                   line => $self->{line}, column => $self->{column}};
1051          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1052          !!!next-input-character;          !!!next-input-character;
1053          redo A;          redo A;
# Line 836  sub _get_next_token ($) { Line 1057  sub _get_next_token ($) {
1057          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
1058              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
1059            !!!cp (57);            !!!cp (57);
1060            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
1061            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
1062          } else {          } else {
1063            !!!cp (58);            !!!cp (58);
# Line 865  sub _get_next_token ($) { Line 1086  sub _get_next_token ($) {
1086          $before_leave->();          $before_leave->();
1087          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1088            !!!cp (61);            !!!cp (61);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1089            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1090          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1091            !!!cp (62);            !!!cp (62);
# Line 891  sub _get_next_token ($) { Line 1110  sub _get_next_token ($) {
1110          !!!next-input-character;          !!!next-input-character;
1111          redo A;          redo A;
1112        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1113            !!!cp (64);
1114          $before_leave->();          $before_leave->();
1115            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1116          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (64);  
           #  
         } else {  
           !!!cp (65);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1117          redo A;          redo A;
1118        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1119          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1120          $before_leave->();          $before_leave->();
1121          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1122            !!!cp (66);            !!!cp (66);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1123            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1124          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1125            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 963  sub _get_next_token ($) { Line 1170  sub _get_next_token ($) {
1170        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1171          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1172            !!!cp (73);            !!!cp (73);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1173            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1174          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1175            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 987  sub _get_next_token ($) { Line 1192  sub _get_next_token ($) {
1192        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{next_char} and
1193                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1194          !!!cp (76);          !!!cp (76);
1195          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{current_attribute}
1196                                value => ''};              = {name => chr ($self->{next_char} + 0x0020),
1197                   value => '',
1198                   line => $self->{line}, column => $self->{column}};
1199          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1200          !!!next-input-character;          !!!next-input-character;
1201          redo A;          redo A;
1202        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1203            !!!cp (77);
1204            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1205          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (77);  
           #  
         } else {  
           !!!cp (78);  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1206          redo A;          redo A;
1207        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
1208          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1209          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1210            !!!cp (79);            !!!cp (79);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1211            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1212          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1213            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1035  sub _get_next_token ($) { Line 1229  sub _get_next_token ($) {
1229          redo A;          redo A;
1230        } else {        } else {
1231          !!!cp (82);          !!!cp (82);
1232          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{current_attribute}
1233                                value => ''};              = {name => chr ($self->{next_char}),
1234                   value => '',
1235                   line => $self->{line}, column => $self->{column}};
1236          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1237          !!!next-input-character;          !!!next-input-character;
1238          redo A;                  redo A;        
# Line 1069  sub _get_next_token ($) { Line 1265  sub _get_next_token ($) {
1265        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1266          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1267            !!!cp (87);            !!!cp (87);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1268            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1269          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1270            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1094  sub _get_next_token ($) { Line 1288  sub _get_next_token ($) {
1288          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1289          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1290            !!!cp (90);            !!!cp (90);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1291            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1292          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1293            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1143  sub _get_next_token ($) { Line 1335  sub _get_next_token ($) {
1335          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1336          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1337            !!!cp (97);            !!!cp (97);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1338            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1339          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1340            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1187  sub _get_next_token ($) { Line 1377  sub _get_next_token ($) {
1377          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1378          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1379            !!!cp (103);            !!!cp (103);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1380            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1381          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1382            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1234  sub _get_next_token ($) { Line 1422  sub _get_next_token ($) {
1422        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1423          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1424            !!!cp (109);            !!!cp (109);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1425            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1426          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1427            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1259  sub _get_next_token ($) { Line 1445  sub _get_next_token ($) {
1445          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1446          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1447            !!!cp (112);            !!!cp (112);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1448            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1449          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1450            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1331  sub _get_next_token ($) { Line 1515  sub _get_next_token ($) {
1515        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1516          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1517            !!!cp (119);            !!!cp (119);
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
1518            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1519          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1520            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
# Line 1353  sub _get_next_token ($) { Line 1535  sub _get_next_token ($) {
1535    
1536          redo A;          redo A;
1537        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1538            !!!cp (122);
1539            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1540          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (122);  
           #  
         } else {  
           !!!cp (123);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1541          redo A;          redo A;
1542        } else {        } else {
1543          !!!cp (124);          !!!cp ('124.1');
1544          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
1545          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1546          ## reconsume          ## reconsume
1547          redo A;          redo A;
1548        }        }
1549        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
1550          if ($self->{next_char} == 0x003E) { # >
1551            if ($self->{current_token}->{type} == END_TAG_TOKEN) {
1552              !!!cp ('124.2');
1553              !!!parse-error (type => 'nestc', token => $self->{current_token});
1554              ## TODO: Different type than slash in start tag
1555              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1556              if ($self->{current_token}->{attributes}) {
1557                !!!cp ('124.4');
1558                !!!parse-error (type => 'end tag attribute');
1559              } else {
1560                !!!cp ('124.5');
1561              }
1562              ## TODO: Test |<title></title/>|
1563            } else {
1564              !!!cp ('124.3');
1565              $self->{self_closing} = 1;
1566            }
1567    
1568            $self->{state} = DATA_STATE;
1569            !!!next-input-character;
1570    
1571            !!!emit ($self->{current_token}); # start tag or end tag
1572    
1573            redo A;
1574          } else {
1575            !!!cp ('124.4');
1576            !!!parse-error (type => 'nestc');
1577            ## TODO: This error type is wrong.
1578            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1579            ## Reconsume.
1580            redo A;
1581          }
1582      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1583        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1584                
1585        my $token = {type => COMMENT_TOKEN, data => ''};        ## NOTE: Set by the previous state
1586          #my $token = {type => COMMENT_TOKEN, data => ''};
1587    
1588        BC: {        BC: {
1589          if ($self->{next_char} == 0x003E) { # >          if ($self->{next_char} == 0x003E) { # >
# Line 1385  sub _get_next_token ($) { Line 1591  sub _get_next_token ($) {
1591            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1592            !!!next-input-character;            !!!next-input-character;
1593    
1594            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1595    
1596            redo A;            redo A;
1597          } elsif ($self->{next_char} == -1) {          } elsif ($self->{next_char} == -1) {
# Line 1393  sub _get_next_token ($) { Line 1599  sub _get_next_token ($) {
1599            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1600            ## reconsume            ## reconsume
1601    
1602            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
1603    
1604            redo A;            redo A;
1605          } else {          } else {
1606            !!!cp (126);            !!!cp (126);
1607            $token->{data} .= chr ($self->{next_char});            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
1608            !!!next-input-character;            !!!next-input-character;
1609            redo BC;            redo BC;
1610          }          }
# Line 1408  sub _get_next_token ($) { Line 1614  sub _get_next_token ($) {
1614      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1615        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1616    
1617          my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);
1618    
1619        my @next_char;        my @next_char;
1620        push @next_char, $self->{next_char};        push @next_char, $self->{next_char};
1621                
# Line 1416  sub _get_next_token ($) { Line 1624  sub _get_next_token ($) {
1624          push @next_char, $self->{next_char};          push @next_char, $self->{next_char};
1625          if ($self->{next_char} == 0x002D) { # -          if ($self->{next_char} == 0x002D) { # -
1626            !!!cp (127);            !!!cp (127);
1627            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};            $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1628                                        line => $l, column => $c,
1629                                       };
1630            $self->{state} = COMMENT_START_STATE;            $self->{state} = COMMENT_START_STATE;
1631            !!!next-input-character;            !!!next-input-character;
1632            redo A;            redo A;
# Line 1452  sub _get_next_token ($) { Line 1662  sub _get_next_token ($) {
1662                      !!!cp (129);                      !!!cp (129);
1663                      ## TODO: What a stupid code this is!                      ## TODO: What a stupid code this is!
1664                      $self->{state} = DOCTYPE_STATE;                      $self->{state} = DOCTYPE_STATE;
1665                        $self->{current_token} = {type => DOCTYPE_TOKEN,
1666                                                  quirks => 1,
1667                                                  line => $l, column => $c,
1668                                                 };
1669                      !!!next-input-character;                      !!!next-input-character;
1670                      redo A;                      redo A;
1671                    } else {                    } else {
# Line 1472  sub _get_next_token ($) { Line 1686  sub _get_next_token ($) {
1686          } else {          } else {
1687            !!!cp (135);            !!!cp (135);
1688          }          }
1689          } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1690                   $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
1691                   $self->{next_char} == 0x005B) { # [
1692            !!!next-input-character;
1693            push @next_char, $self->{next_char};
1694            if ($self->{next_char} == 0x0043) { # C
1695              !!!next-input-character;
1696              push @next_char, $self->{next_char};
1697              if ($self->{next_char} == 0x0044) { # D
1698                !!!next-input-character;
1699                push @next_char, $self->{next_char};
1700                if ($self->{next_char} == 0x0041) { # A
1701                  !!!next-input-character;
1702                  push @next_char, $self->{next_char};
1703                  if ($self->{next_char} == 0x0054) { # T
1704                    !!!next-input-character;
1705                    push @next_char, $self->{next_char};
1706                    if ($self->{next_char} == 0x0041) { # A
1707                      !!!next-input-character;
1708                      push @next_char, $self->{next_char};
1709                      if ($self->{next_char} == 0x005B) { # [
1710                        !!!cp (135.1);
1711                        $self->{state} = CDATA_BLOCK_STATE;
1712                        !!!next-input-character;
1713                        redo A;
1714                      } else {
1715                        !!!cp (135.2);
1716                      }
1717                    } else {
1718                      !!!cp (135.3);
1719                    }
1720                  } else {
1721                    !!!cp (135.4);                
1722                  }
1723                } else {
1724                  !!!cp (135.5);
1725                }
1726              } else {
1727                !!!cp (135.6);
1728              }
1729            } else {
1730              !!!cp (135.7);
1731            }
1732        } else {        } else {
1733          !!!cp (136);          !!!cp (136);
1734        }        }
# Line 1480  sub _get_next_token ($) { Line 1737  sub _get_next_token ($) {
1737        $self->{next_char} = shift @next_char;        $self->{next_char} = shift @next_char;
1738        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1739        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
1740          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1741                                    line => $l, column => $c,
1742                                   };
1743        redo A;        redo A;
1744                
1745        ## ISSUE: typos in spec: chacacters, is is a parse error        ## ISSUE: typos in spec: chacacters, is is a parse error
# Line 1603  sub _get_next_token ($) { Line 1863  sub _get_next_token ($) {
1863          redo A;          redo A;
1864        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
1865          !!!cp (152);          !!!cp (152);
1866          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
1867                            line => $self->{line_prev},
1868                            column => $self->{column_prev});
1869          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
1870          ## Stay in the state          ## Stay in the state
1871          !!!next-input-character;          !!!next-input-character;
# Line 1619  sub _get_next_token ($) { Line 1881  sub _get_next_token ($) {
1881          redo A;          redo A;
1882        } else {        } else {
1883          !!!cp (154);          !!!cp (154);
1884          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
1885                            line => $self->{line_prev},
1886                            column => $self->{column_prev});
1887          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
1888          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
1889          !!!next-input-character;          !!!next-input-character;
# Line 1658  sub _get_next_token ($) { Line 1922  sub _get_next_token ($) {
1922          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1923          !!!next-input-character;          !!!next-input-character;
1924    
1925          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1926    
1927          redo A;          redo A;
1928        } elsif ($self->{next_char} == -1) {        } elsif ($self->{next_char} == -1) {
# Line 1667  sub _get_next_token ($) { Line 1931  sub _get_next_token ($) {
1931          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1932          ## reconsume          ## reconsume
1933    
1934          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
1935    
1936          redo A;          redo A;
1937        } else {        } else {
1938          !!!cp (160);          !!!cp (160);
1939          $self->{current_token}          $self->{current_token}->{name} = chr $self->{next_char};
1940              = {type => DOCTYPE_TOKEN,          delete $self->{current_token}->{quirks};
                name => chr ($self->{next_char}),  
                #quirks => 0,  
               };  
1941  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
1942          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
1943          !!!next-input-character;          !!!next-input-character;
# Line 2192  sub _get_next_token ($) { Line 2453  sub _get_next_token ($) {
2453          !!!next-input-character;          !!!next-input-character;
2454          redo A;          redo A;
2455        }        }
2456        } elsif ($self->{state} == CDATA_BLOCK_STATE) {
2457          my $s = '';
2458          
2459          my ($l, $c) = ($self->{line}, $self->{column});
2460    
2461          CS: while ($self->{next_char} != -1) {
2462            if ($self->{next_char} == 0x005D) { # ]
2463              !!!next-input-character;
2464              if ($self->{next_char} == 0x005D) { # ]
2465                !!!next-input-character;
2466                MDC: {
2467                  if ($self->{next_char} == 0x003E) { # >
2468                    !!!cp (221.1);
2469                    !!!next-input-character;
2470                    last CS;
2471                  } elsif ($self->{next_char} == 0x005D) { # ]
2472                    !!!cp (221.2);
2473                    $s .= ']';
2474                    !!!next-input-character;
2475                    redo MDC;
2476                  } else {
2477                    !!!cp (221.3);
2478                    $s .= ']]';
2479                    #
2480                  }
2481                } # MDC
2482              } else {
2483                !!!cp (221.4);
2484                $s .= ']';
2485                #
2486              }
2487            } else {
2488              !!!cp (221.5);
2489              #
2490            }
2491            $s .= chr $self->{next_char};
2492            !!!next-input-character;
2493          } # CS
2494    
2495          $self->{state} = DATA_STATE;
2496          ## next-input-character done or EOF, which is reconsumed.
2497    
2498          if (length $s) {
2499            !!!cp (221.6);
2500            !!!emit ({type => CHARACTER_TOKEN, data => $s,
2501                      line => $l, column => $c});
2502          } else {
2503            !!!cp (221.7);
2504          }
2505    
2506          redo A;
2507    
2508          ## ISSUE: "text tokens" in spec.
2509          ## TODO: Streaming support
2510      } else {      } else {
2511        die "$0: $self->{state}: Unknown state";        die "$0: $self->{state}: Unknown state";
2512      }      }
# Line 2203  sub _get_next_token ($) { Line 2518  sub _get_next_token ($) {
2518  sub _tokenize_attempt_to_consume_an_entity ($$$) {  sub _tokenize_attempt_to_consume_an_entity ($$$) {
2519    my ($self, $in_attr, $additional) = @_;    my ($self, $in_attr, $additional) = @_;
2520    
2521      my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
2522    
2523    if ({    if ({
2524         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
2525         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
# Line 2243  sub _tokenize_attempt_to_consume_an_enti Line 2560  sub _tokenize_attempt_to_consume_an_enti
2560            redo X;            redo X;
2561          } elsif (not defined $code) { # no hexadecimal digit          } elsif (not defined $code) { # no hexadecimal digit
2562            !!!cp (1005);            !!!cp (1005);
2563            !!!parse-error (type => 'bare hcro');            !!!parse-error (type => 'bare hcro', line => $l, column => $c);
2564            !!!back-next-input-character ($x_char, $self->{next_char});            !!!back-next-input-character ($x_char, $self->{next_char});
2565            $self->{next_char} = 0x0023; # #            $self->{next_char} = 0x0023; # #
2566            return undef;            return undef;
# Line 2252  sub _tokenize_attempt_to_consume_an_enti Line 2569  sub _tokenize_attempt_to_consume_an_enti
2569            !!!next-input-character;            !!!next-input-character;
2570          } else {          } else {
2571            !!!cp (1007);            !!!cp (1007);
2572            !!!parse-error (type => 'no refc');            !!!parse-error (type => 'no refc', line => $l, column => $c);
2573          }          }
2574    
2575          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2576            !!!cp (1008);            !!!cp (1008);
2577            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2578            $code = 0xFFFD;            $code = 0xFFFD;
2579          } elsif ($code > 0x10FFFF) {          } elsif ($code > 0x10FFFF) {
2580            !!!cp (1009);            !!!cp (1009);
2581            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);            !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2582            $code = 0xFFFD;            $code = 0xFFFD;
2583          } elsif ($code == 0x000D) {          } elsif ($code == 0x000D) {
2584            !!!cp (1010);            !!!cp (1010);
2585            !!!parse-error (type => 'CR character reference');            !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2586            $code = 0x000A;            $code = 0x000A;
2587          } elsif (0x80 <= $code and $code <= 0x9F) {          } elsif (0x80 <= $code and $code <= 0x9F) {
2588            !!!cp (1011);            !!!cp (1011);
2589            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);            !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2590            $code = $c1_entity_char->{$code};            $code = $c1_entity_char->{$code};
2591          }          }
2592    
2593          return {type => CHARACTER_TOKEN, data => chr $code,          return {type => CHARACTER_TOKEN, data => chr $code,
2594                  has_reference => 1};                  has_reference => 1,
2595                    line => $l, column => $c,
2596                   };
2597        } # X        } # X
2598      } elsif (0x0030 <= $self->{next_char} and      } elsif (0x0030 <= $self->{next_char} and
2599               $self->{next_char} <= 0x0039) { # 0..9               $self->{next_char} <= 0x0039) { # 0..9
# Line 2295  sub _tokenize_attempt_to_consume_an_enti Line 2614  sub _tokenize_attempt_to_consume_an_enti
2614          !!!next-input-character;          !!!next-input-character;
2615        } else {        } else {
2616          !!!cp (1014);          !!!cp (1014);
2617          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc', line => $l, column => $c);
2618        }        }
2619    
2620        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
2621          !!!cp (1015);          !!!cp (1015);
2622          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);
2623          $code = 0xFFFD;          $code = 0xFFFD;
2624        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
2625          !!!cp (1016);          !!!cp (1016);
2626          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);
2627          $code = 0xFFFD;          $code = 0xFFFD;
2628        } elsif ($code == 0x000D) {        } elsif ($code == 0x000D) {
2629          !!!cp (1017);          !!!cp (1017);
2630          !!!parse-error (type => 'CR character reference');          !!!parse-error (type => 'CR character reference', line => $l, column => $c);
2631          $code = 0x000A;          $code = 0x000A;
2632        } elsif (0x80 <= $code and $code <= 0x9F) {        } elsif (0x80 <= $code and $code <= 0x9F) {
2633          !!!cp (1018);          !!!cp (1018);
2634          !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);
2635          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
2636        }        }
2637                
2638        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
2639                  line => $l, column => $c,
2640                 };
2641      } else {      } else {
2642        !!!cp (1019);        !!!cp (1019);
2643        !!!parse-error (type => 'bare nero');        !!!parse-error (type => 'bare nero', line => $l, column => $c);
2644        !!!back-next-input-character ($self->{next_char});        !!!back-next-input-character ($self->{next_char});
2645        $self->{next_char} = 0x0023; # #        $self->{next_char} = 0x0023; # #
2646        return undef;        return undef;
# Line 2336  sub _tokenize_attempt_to_consume_an_enti Line 2657  sub _tokenize_attempt_to_consume_an_enti
2657      require Whatpm::_NamedEntityList;      require Whatpm::_NamedEntityList;
2658      our $EntityChar;      our $EntityChar;
2659    
2660      while (length $entity_name < 10 and      while (length $entity_name < 30 and
2661             ## NOTE: Some number greater than the maximum length of entity name             ## NOTE: Some number greater than the maximum length of entity name
2662             ((0x0041 <= $self->{next_char} and # a             ((0x0041 <= $self->{next_char} and # a
2663               $self->{next_char} <= 0x005A) or # x               $self->{next_char} <= 0x005A) or # x
# Line 2369  sub _tokenize_attempt_to_consume_an_enti Line 2690  sub _tokenize_attempt_to_consume_an_enti
2690            
2691      if ($match > 0) {      if ($match > 0) {
2692        !!!cp (1023);        !!!cp (1023);
2693        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2694                  line => $l, column => $c,
2695                 };
2696      } elsif ($match < 0) {      } elsif ($match < 0) {
2697        !!!parse-error (type => 'no refc');        !!!parse-error (type => 'no refc', line => $l, column => $c);
2698        if ($in_attr and $match < -1) {        if ($in_attr and $match < -1) {
2699          !!!cp (1024);          !!!cp (1024);
2700          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
2701                    line => $l, column => $c,
2702                   };
2703        } else {        } else {
2704          !!!cp (1025);          !!!cp (1025);
2705          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
2706                    line => $l, column => $c,
2707                   };
2708        }        }
2709      } else {      } else {
2710        !!!cp (1026);        !!!cp (1026);
2711        !!!parse-error (type => 'bare ero');        !!!parse-error (type => 'bare ero', line => $l, column => $c);
2712        ## NOTE: "No characters are consumed" in the spec.        ## NOTE: "No characters are consumed" in the spec.
2713        return {type => CHARACTER_TOKEN, data => '&'.$value};        return {type => CHARACTER_TOKEN, data => '&'.$value,
2714                  line => $l, column => $c,
2715                 };
2716      }      }
2717    } else {    } else {
2718      !!!cp (1027);      !!!cp (1027);
2719      ## no characters are consumed      ## no characters are consumed
2720      !!!parse-error (type => 'bare ero');      !!!parse-error (type => 'bare ero', line => $l, column => $c);
2721      return undef;      return undef;
2722    }    }
2723  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 2459  sub _tree_construction_initial ($) { Line 2788  sub _tree_construction_initial ($) {
2788            defined $token->{public_identifier} or            defined $token->{public_identifier} or
2789            defined $token->{system_identifier}) {            defined $token->{system_identifier}) {
2790          !!!cp ('t1');          !!!cp ('t1');
2791          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
2792        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
2793          !!!cp ('t2');          !!!cp ('t2');
2794          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)
2795          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
2796        } else {        } else {
2797          !!!cp ('t3');          !!!cp ('t3');
2798        }        }
2799                
2800        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
2801          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
2802          ## NOTE: Default value for both |public_id| and |system_id| attributes
2803          ## are empty strings, so that we don't set any value in missing cases.
2804        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{public_identifier})
2805            if defined $token->{public_identifier};            if defined $token->{public_identifier};
2806        $doctype->system_id ($token->{system_identifier})        $doctype->system_id ($token->{system_identifier})
# Line 2602  sub _tree_construction_initial ($) { Line 2933  sub _tree_construction_initial ($) {
2933                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
2934               }->{$token->{type}}) {               }->{$token->{type}}) {
2935        !!!cp ('t14');        !!!cp ('t14');
2936        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
2937        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2938        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
2939        ## reprocess        ## reprocess
2940          !!!ack-later;
2941        return;        return;
2942      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
2943        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
# Line 2623  sub _tree_construction_initial ($) { Line 2955  sub _tree_construction_initial ($) {
2955          !!!cp ('t17');          !!!cp ('t17');
2956        }        }
2957    
2958        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
2959        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2960        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
2961        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 2984  sub _tree_construction_root_element ($)
2984    B: {    B: {
2985        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
2986          !!!cp ('t19');          !!!cp ('t19');
2987          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
2988          ## Ignore the token          ## Ignore the token
2989          ## Stay in the insertion mode.          ## Stay in the insertion mode.
2990          !!!next-token;          !!!next-token;
# Line 2686  sub _tree_construction_root_element ($) Line 3018  sub _tree_construction_root_element ($)
3018        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3019          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3020            my $root_element;            my $root_element;
3021            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3022            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3023            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3024                  [$root_element, $el_category->{html}];
3025    
3026            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3027              !!!cp ('t24');              !!!cp ('t24');
3028              $self->{application_cache_selection}              $self->{application_cache_selection}
3029                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3030              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3031                ## According to Hixie (#whatwg 2008-03-19), it should be
3032                ## resolved against the base URI of the document in HTML
3033                ## or xml:base of the element in XHTML.
3034            } else {            } else {
3035              !!!cp ('t25');              !!!cp ('t25');
3036              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3037            }            }
3038    
3039              !!!nack ('t25c');
3040    
3041            !!!next-token;            !!!next-token;
3042            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3043          } else {          } else {
# Line 2716  sub _tree_construction_root_element ($) Line 3054  sub _tree_construction_root_element ($)
3054          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3055        }        }
3056    
3057      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3058        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3059      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3060      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3061    
3062      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3063    
3064      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3065        !!!ack-later;
3066      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
3067    
3068      ## ISSUE: There is an issue in the spec      ## ISSUE: There is an issue in the spec
# Line 2743  sub _reset_insertion_mode ($) { Line 3083  sub _reset_insertion_mode ($) {
3083            
3084      ## Step 3      ## Step 3
3085      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"!?  
3086        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3087          $last = 1;          $last = 1;
3088          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3089            if ($self->{inner_html_node}->[1] eq 'td' or            if ($self->{inner_html_node}->[1] & TABLE_CELL_EL) {
               $self->{inner_html_node}->[1] eq 'th') {  
3090              !!!cp ('t27');              !!!cp ('t27');
3091              #              #
3092            } else {            } else {
# Line 2762  sub _reset_insertion_mode ($) { Line 3096  sub _reset_insertion_mode ($) {
3096          }          }
3097        }        }
3098            
3099        ## Step 4..13      ## Step 4..14
3100        my $new_mode = {      my $new_mode;
3101        if ($node->[1] & FOREIGN_EL) {
3102          ## NOTE: Strictly spaking, the line below only applies to MathML and
3103          ## SVG elements.  Currently the HTML syntax supports only MathML and
3104          ## SVG elements as foreigners.
3105          $new_mode = $self->{insertion_mode} | IN_FOREIGN_CONTENT_IM;
3106          ## ISSUE: What is set as the secondary insertion mode?
3107        } else {
3108          $new_mode = {
3109                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3110                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3111                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
# Line 2779  sub _reset_insertion_mode ($) { Line 3121  sub _reset_insertion_mode ($) {
3121                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3122                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3123                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3124                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3125        $self->{insertion_mode} = $new_mode and return if defined $new_mode;      }
3126        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3127                
3128        ## Step 14        ## Step 15
3129        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3130          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3131            !!!cp ('t29');            !!!cp ('t29');
3132            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2797  sub _reset_insertion_mode ($) { Line 3140  sub _reset_insertion_mode ($) {
3140          !!!cp ('t31');          !!!cp ('t31');
3141        }        }
3142                
3143        ## Step 15        ## Step 16
3144        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3145                
3146        ## Step 16        ## Step 17
3147        $i--;        $i--;
3148        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3149                
3150        ## Step 17        ## Step 18
3151        redo S3;        redo S3;
3152      } # S3      } # S3
3153    
# Line 2908  sub _tree_construction_main ($) { Line 3251  sub _tree_construction_main ($) {
3251      !!!cp ('t39');      !!!cp ('t39');
3252    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3253    
3254    my $parse_rcdata = sub ($$) {    my $insert;
3255      my ($content_model_flag, $insert) = @_;  
3256      my $parse_rcdata = sub ($) {
3257        my ($content_model_flag) = @_;
3258    
3259      ## Step 1      ## Step 1
3260      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3261      my $el;      my $el;
3262      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3263    
3264      ## Step 2      ## Step 2
3265      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3266    
3267      ## Step 3      ## Step 3
3268      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2925  sub _tree_construction_main ($) { Line 3270  sub _tree_construction_main ($) {
3270    
3271      ## Step 4      ## Step 4
3272      my $text = '';      my $text = '';
3273        !!!nack ('t40.1');
3274      !!!next-token;      !!!next-token;
3275      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3276        !!!cp ('t40');        !!!cp ('t40');
# Line 2947  sub _tree_construction_main ($) { Line 3293  sub _tree_construction_main ($) {
3293          $token->{tag_name} eq $start_tag_name) {          $token->{tag_name} eq $start_tag_name) {
3294        !!!cp ('t42');        !!!cp ('t42');
3295        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!cp ('t43');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!cp ('t44');  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
3296      } else {      } else {
3297        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
3298          if ($content_model_flag == CDATA_CONTENT_MODEL) {
3299            !!!cp ('t43');
3300            !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3301          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
3302            !!!cp ('t44');
3303            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
3304          } else {
3305            die "$0: $content_model_flag in parse_rcdata";
3306          }
3307      }      }
3308      !!!next-token;      !!!next-token;
3309    }; # $parse_rcdata    }; # $parse_rcdata
3310    
3311    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
3312      my $script_el;      my $script_el;
3313      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
3314      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
3315    
3316      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
3317      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3318            
3319      my $text = '';      my $text = '';
3320        !!!nack ('t45.1');
3321      !!!next-token;      !!!next-token;
3322      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
3323        !!!cp ('t45');        !!!cp ('t45');
# Line 2988  sub _tree_construction_main ($) { Line 3337  sub _tree_construction_main ($) {
3337        ## Ignore the token        ## Ignore the token
3338      } else {      } else {
3339        !!!cp ('t48');        !!!cp ('t48');
3340        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);
3341        ## ISSUE: And ignore?        ## ISSUE: And ignore?
3342        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3343      }      }
# Line 3011  sub _tree_construction_main ($) { Line 3360  sub _tree_construction_main ($) {
3360      !!!next-token;      !!!next-token;
3361    }; # $script_start_tag    }; # $script_start_tag
3362    
3363      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
3364      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
3365      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
3366    
3367    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
3368      my $tag_name = shift;      my $end_tag_token = shift;
3369        my $tag_name = $end_tag_token->{tag_name};
3370    
3371        ## NOTE: The adoption agency algorithm (AAA).
3372    
3373      FET: {      FET: {
3374        ## Step 1        ## Step 1
3375        my $formatting_element;        my $formatting_element;
3376        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
3377        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3378          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3379              !!!cp ('t52');
3380              last AFE;
3381            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
3382                         eq $tag_name) {
3383            !!!cp ('t51');            !!!cp ('t51');
3384            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
3385            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
3386            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
3387          }          }
3388        } # AFE        } # AFE
3389        unless (defined $formatting_element) {        unless (defined $formatting_element) {
3390          !!!cp ('t53');          !!!cp ('t53');
3391          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);
3392          ## Ignore the token          ## Ignore the token
3393          !!!next-token;          !!!next-token;
3394          return;          return;
# Line 3048  sub _tree_construction_main ($) { Line 3405  sub _tree_construction_main ($) {
3405              last INSCOPE;              last INSCOPE;
3406            } else { # in open elements but not in scope            } else { # in open elements but not in scope
3407              !!!cp ('t55');              !!!cp ('t55');
3408              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3409                                token => $end_tag_token);
3410              ## Ignore the token              ## Ignore the token
3411              !!!next-token;              !!!next-token;
3412              return;              return;
3413            }            }
3414          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
3415            !!!cp ('t56');            !!!cp ('t56');
3416            $in_scope = 0;            $in_scope = 0;
3417          }          }
3418        } # INSCOPE        } # INSCOPE
3419        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
3420          !!!cp ('t57');          !!!cp ('t57');
3421          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},
3422                            token => $end_tag_token);
3423          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
3424          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
3425          return;          return;
3426        }        }
3427        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
3428          !!!cp ('t58');          !!!cp ('t58');
3429          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!parse-error (type => 'not closed',
3430                            value => $self->{open_elements}->[-1]->[0]
3431                                ->manakai_local_name,
3432                            token => $end_tag_token);
3433        }        }
3434                
3435        ## Step 2        ## Step 2
# Line 3078  sub _tree_construction_main ($) { Line 3437  sub _tree_construction_main ($) {
3437        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
3438        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3439          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3440          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
3441              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
3442              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
3443               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
3444            !!!cp ('t59');            !!!cp ('t59');
3445            $furthest_block = $node;            $furthest_block = $node;
3446            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3167  sub _tree_construction_main ($) { Line 3526  sub _tree_construction_main ($) {
3526        } # S7          } # S7  
3527                
3528        ## Step 8        ## Step 8
3529        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
3530            my $foster_parent_element;
3531            my $next_sibling;
3532            OE: for (reverse 0..$#{$self->{open_elements}}) {
3533              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
3534                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3535                                 if (defined $parent and $parent->node_type == 1) {
3536                                   !!!cp ('t65.1');
3537                                   $foster_parent_element = $parent;
3538                                   $next_sibling = $self->{open_elements}->[$_]->[0];
3539                                 } else {
3540                                   !!!cp ('t65.2');
3541                                   $foster_parent_element
3542                                     = $self->{open_elements}->[$_ - 1]->[0];
3543                                 }
3544                                 last OE;
3545                               }
3546                             } # OE
3547                             $foster_parent_element = $self->{open_elements}->[0]->[0]
3548                               unless defined $foster_parent_element;
3549            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
3550            $open_tables->[-1]->[1] = 1; # tainted
3551          } else {
3552            !!!cp ('t65.3');
3553            $common_ancestor_node->[0]->append_child ($last_node->[0]);
3554          }
3555                
3556        ## Step 9        ## Step 9
3557        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3213  sub _tree_construction_main ($) { Line 3597  sub _tree_construction_main ($) {
3597      } # FET      } # FET
3598    }; # $formatting_end_tag    }; # $formatting_end_tag
3599    
3600    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
3601      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
3602    }; # $insert_to_current    }; # $insert_to_current
3603    
3604    my $insert_to_foster = sub {    my $insert_to_foster = sub {
3605                         my $child = shift;      my $child = shift;
3606                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3607                              table => 1, tbody => 1, tfoot => 1,        # MUST
3608                              thead => 1, tr => 1,        my $foster_parent_element;
3609                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
3610                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
3611                           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') {  
3612                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3613                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
3614                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3245  sub _tree_construction_main ($) { Line 3626  sub _tree_construction_main ($) {
3626                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
3627                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
3628                             ($child, $next_sibling);                             ($child, $next_sibling);
3629                         } else {        $open_tables->[-1]->[1] = 1; # tainted
3630                           !!!cp ('t72');      } else {
3631                           $self->{open_elements}->[-1]->[0]->append_child ($child);        !!!cp ('t72');
3632                         }        $self->{open_elements}->[-1]->[0]->append_child ($child);
3633        }
3634    }; # $insert_to_foster    }; # $insert_to_foster
3635    
3636    my $insert;    B: while (1) {
   
   B: {  
3637      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3638        !!!cp ('t73');        !!!cp ('t73');
3639        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);
3640        ## Ignore the token        ## Ignore the token
3641        ## Stay in the phase        ## Stay in the phase
3642        !!!next-token;        !!!next-token;
3643        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         !!!cp ('t74');  
         #  
       } else {  
         ## Generate implied end tags  
         while ({  
                 dd => 1, dt => 1, li => 1, p => 1,  
                }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!cp ('t75');  
           pop @{$self->{open_elements}};  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!cp ('t76');  
           !!!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') {  
 ## ISSUE: This case is never reached.  
           !!!cp ('t77');  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } else {  
           !!!cp ('t78');  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
3644      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
3645               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
3646        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
3647          !!!cp ('t79');          !!!cp ('t79');
3648          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html', token => $token);
3649          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
3650        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
3651          !!!cp ('t80');          !!!cp ('t80');
3652          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html', token => $token);
3653          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
3654        } else {        } else {
3655          !!!cp ('t81');          !!!cp ('t81');
3656        }        }
3657    
3658        !!!cp ('t82');        !!!cp ('t82');
3659        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
3660        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
3661        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
3662          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3318  sub _tree_construction_main ($) { Line 3666  sub _tree_construction_main ($) {
3666               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
3667          }          }
3668        }        }
3669          !!!nack ('t84.1');
3670        !!!next-token;        !!!next-token;
3671        redo B;        next B;
3672      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3673        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3674        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3333  sub _tree_construction_main ($) { Line 3682  sub _tree_construction_main ($) {
3682          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
3683        }        }
3684        !!!next-token;        !!!next-token;
3685        redo B;        next B;
3686      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
3687          if ($token->{type} == CHARACTER_TOKEN) {
3688            !!!cp ('t87.1');
3689            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3690            !!!next-token;
3691            next B;
3692          } elsif ($token->{type} == START_TAG_TOKEN) {
3693            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
3694                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
3695                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
3696                ($token->{tag_name} eq 'svg' and
3697                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
3698              ## NOTE: "using the rules for secondary insertion mode"then"continue"
3699              !!!cp ('t87.2');
3700              #
3701            } elsif ({
3702                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
3703                      center => 1, code => 1, dd => 1, div => 1, dl => 1, em => 1,
3704                      embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1, ## No h4!
3705                      h5 => 1, h6 => 1, head => 1, hr => 1, i => 1, img => 1,
3706                      li => 1, menu => 1, meta => 1, nobr => 1, p => 1, pre => 1,
3707                      ruby => 1, s => 1, small => 1, span => 1, strong => 1,
3708                      sub => 1, sup => 1, table => 1, tt => 1, u => 1, ul => 1,
3709                      var => 1,
3710                     }->{$token->{tag_name}}) {
3711              !!!cp ('t87.2');
3712              !!!parse-error (type => 'not closed',
3713                              value => $self->{open_elements}->[-1]->[0]
3714                                  ->manakai_local_name,
3715                              token => $token);
3716    
3717              pop @{$self->{open_elements}}
3718                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
3719    
3720              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
3721              ## Reprocess.
3722              next B;
3723            } else {
3724              ## TODO: case fixup
3725    
3726              !!!insert-element-f ($self->{open_elements}->[-1]->[0]->namespace_uri, $token);
3727    
3728              if ($self->{self_closing}) {
3729                pop @{$self->{open_elements}};
3730                !!!ack ('t87.3');
3731              } else {
3732                !!!cp ('t87.4');
3733              }
3734    
3735              !!!next-token;
3736              next B;
3737            }
3738          } elsif ($token->{type} == END_TAG_TOKEN) {
3739            ## NOTE: "using the rules for secondary insertion mode" then "continue"
3740            !!!cp ('t87.5');
3741            #
3742          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3743            ## NOTE: "using the rules for secondary insertion mode" then "continue"
3744            !!!cp ('t87.6');
3745            #
3746            ## TODO: ...
3747          } else {
3748            die "$0: $token->{type}: Unknown token type";        
3749          }
3750        }
3751    
3752        if ($self->{insertion_mode} & HEAD_IMS) {
3753        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
3754          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3755            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3756                !!!cp ('t88.2');
3757                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3758              } else {
3759                !!!cp ('t88.1');
3760                ## Ignore the token.
3761                !!!next-token;
3762                next B;
3763              }
3764            unless (length $token->{data}) {            unless (length $token->{data}) {
3765              !!!cp ('t88');              !!!cp ('t88');
3766              !!!next-token;              !!!next-token;
3767              redo B;              next B;
3768            }            }
3769          }          }
3770    
3771          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3772            !!!cp ('t89');            !!!cp ('t89');
3773            ## As if <head>            ## As if <head>
3774            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
3775            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3776            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
3777                  [$self->{head_element}, $el_category->{head}];
3778    
3779            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
3780            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3360  sub _tree_construction_main ($) { Line 3784  sub _tree_construction_main ($) {
3784            !!!cp ('t90');            !!!cp ('t90');
3785            ## As if </noscript>            ## As if </noscript>
3786            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
3787            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#character', token => $token);
3788                        
3789            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
3790            ## As if </head>            ## As if </head>
# Line 3376  sub _tree_construction_main ($) { Line 3800  sub _tree_construction_main ($) {
3800            !!!cp ('t92');            !!!cp ('t92');
3801          }          }
3802    
3803              ## "after head" insertion mode          ## "after head" insertion mode
3804              ## As if <body>          ## As if <body>
3805              !!!insert-element ('body');          !!!insert-element ('body',, $token);
3806              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
3807              ## reprocess          ## reprocess
3808              redo B;          next B;
3809            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3810              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
3811                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3812                  !!!cp ('t93');              !!!cp ('t93');
3813                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3814                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
3815                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
3816                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
3817                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
3818                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
3819                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
3820                  !!!cp ('t94');              !!!next-token;
3821                  #              next B;
3822                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3823                  !!!cp ('t95');              !!!cp ('t94');
3824                  !!!parse-error (type => 'in head:head'); # or in head noscript              #
3825                  ## Ignore the token            } else {
3826                  !!!next-token;              !!!cp ('t95');
3827                  redo B;              !!!parse-error (type => 'in head:head', token => $token); # or in head noscript
3828                }              ## Ignore the token
3829              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              !!!nack ('t95.1');
3830                !!!cp ('t96');              !!!next-token;
3831                ## As if <head>              next B;
3832                !!!create-element ($self->{head_element}, 'head');            }
3833                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3834                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!cp ('t96');
3835              ## As if <head>
3836              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
3837              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3838              push @{$self->{open_elements}},
3839                  [$self->{head_element}, $el_category->{head}];
3840    
3841                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
3842                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
3843              } else {          } else {
3844                !!!cp ('t97');            !!!cp ('t97');
3845              }          }
3846    
3847              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
3848                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3849                  !!!cp ('t98');                  !!!cp ('t98');
3850                  ## As if </noscript>                  ## As if </noscript>
3851                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3852                  !!!parse-error (type => 'in noscript:base');                  !!!parse-error (type => 'in noscript:base', token => $token);
3853                                
3854                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3855                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3431  sub _tree_construction_main ($) { Line 3860  sub _tree_construction_main ($) {
3860                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3861                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3862                  !!!cp ('t100');                  !!!cp ('t100');
3863                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3864                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3865                        [$self->{head_element}, $el_category->{head}];
3866                } else {                } else {
3867                  !!!cp ('t101');                  !!!cp ('t101');
3868                }                }
3869                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3870                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3871                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3872                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3873                  !!!nack ('t101.1');
3874                !!!next-token;                !!!next-token;
3875                redo B;                next B;
3876              } elsif ($token->{tag_name} eq 'link') {              } elsif ($token->{tag_name} eq 'link') {
3877                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3878                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3879                  !!!cp ('t102');                  !!!cp ('t102');
3880                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3881                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3882                        [$self->{head_element}, $el_category->{head}];
3883                } else {                } else {
3884                  !!!cp ('t103');                  !!!cp ('t103');
3885                }                }
3886                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3887                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
3888                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3889                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3890                  !!!ack ('t103.1');
3891                !!!next-token;                !!!next-token;
3892                redo B;                next B;
3893              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
3894                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3895                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3896                  !!!cp ('t104');                  !!!cp ('t104');
3897                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3898                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3899                        [$self->{head_element}, $el_category->{head}];
3900                } else {                } else {
3901                  !!!cp ('t105');                  !!!cp ('t105');
3902                }                }
3903                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3904                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.
3905    
3906                unless ($self->{confident}) {                unless ($self->{confident}) {
3907                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) { ## TODO: And if supported
3908                    !!!cp ('t106');                    !!!cp ('t106');
3909                    $self->{change_encoding}                    $self->{change_encoding}
3910                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
3911                             $token);
3912                                        
3913                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
3914                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
# Line 3488  sub _tree_construction_main ($) { Line 3923  sub _tree_construction_main ($) {
3923                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
3924                      !!!cp ('t107');                      !!!cp ('t107');
3925                      $self->{change_encoding}                      $self->{change_encoding}
3926                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
3927                               $token);
3928                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
3929                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
3930                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3514  sub _tree_construction_main ($) { Line 3950  sub _tree_construction_main ($) {
3950                  }                  }
3951                }                }
3952    
3953                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3954                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3955                  !!!ack ('t110.1');
3956                !!!next-token;                !!!next-token;
3957                redo B;                next B;
3958              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
3959                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3960                  !!!cp ('t111');                  !!!cp ('t111');
3961                  ## As if </noscript>                  ## As if </noscript>
3962                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3963                  !!!parse-error (type => 'in noscript:title');                  !!!parse-error (type => 'in noscript:title', token => $token);
3964                                
3965                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
3966                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
3967                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
3968                  !!!cp ('t112');                  !!!cp ('t112');
3969                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3970                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3971                        [$self->{head_element}, $el_category->{head}];
3972                } else {                } else {
3973                  !!!cp ('t113');                  !!!cp ('t113');
3974                }                }
# Line 3538  sub _tree_construction_main ($) { Line 3976  sub _tree_construction_main ($) {
3976                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3977                my $parent = defined $self->{head_element} ? $self->{head_element}                my $parent = defined $self->{head_element} ? $self->{head_element}
3978                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
3979                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL);
3980                                sub { $parent->append_child ($_[0]) });                pop @{$self->{open_elements}} # <head>
               pop @{$self->{open_elements}}  
3981                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3982                redo B;                next B;
3983              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style') {
3984                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
3985                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
3986                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
3987                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
3988                  !!!cp ('t114');                  !!!cp ('t114');
3989                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
3990                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
3991                        [$self->{head_element}, $el_category->{head}];
3992                } else {                } else {
3993                  !!!cp ('t115');                  !!!cp ('t115');
3994                }                }
3995                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
3996                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
3997                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
3998                redo B;                next B;
3999              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4000                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4001                  !!!cp ('t116');                  !!!cp ('t116');
4002                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4003                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4004                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4005                    !!!nack ('t116.1');
4006                  !!!next-token;                  !!!next-token;
4007                  redo B;                  next B;
4008                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4009                  !!!cp ('t117');                  !!!cp ('t117');
4010                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript:noscript', token => $token);
4011                  ## Ignore the token                  ## Ignore the token
4012                    !!!nack ('t117.1');
4013                  !!!next-token;                  !!!next-token;
4014                  redo B;                  next B;
4015                } else {                } else {
4016                  !!!cp ('t118');                  !!!cp ('t118');
4017                  #                  #
# Line 3581  sub _tree_construction_main ($) { Line 4021  sub _tree_construction_main ($) {
4021                  !!!cp ('t119');                  !!!cp ('t119');
4022                  ## As if </noscript>                  ## As if </noscript>
4023                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4024                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:script', token => $token);
4025                                
4026                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4027                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4028                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4029                  !!!cp ('t120');                  !!!cp ('t120');
4030                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);
4031                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4032                        [$self->{head_element}, $el_category->{head}];
4033                } else {                } else {
4034                  !!!cp ('t121');                  !!!cp ('t121');
4035                }                }
4036    
4037                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4038                $script_start_tag->($insert_to_current);                $script_start_tag->();
4039                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4040                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4041                redo B;                next B;
4042              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4043                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4044                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4045                  !!!cp ('t122');                  !!!cp ('t122');
4046                  ## As if </noscript>                  ## As if </noscript>
4047                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4048                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);
4049                                    
4050                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4051                  ## As if </head>                  ## As if </head>
# Line 3621  sub _tree_construction_main ($) { Line 4062  sub _tree_construction_main ($) {
4062                }                }
4063    
4064                ## "after head" insertion mode                ## "after head" insertion mode
4065                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4066                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4067                  !!!cp ('t126');                  !!!cp ('t126');
4068                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3631  sub _tree_construction_main ($) { Line 4072  sub _tree_construction_main ($) {
4072                } else {                } else {
4073                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4074                }                }
4075                  !!!nack ('t127.1');
4076                !!!next-token;                !!!next-token;
4077                redo B;                next B;
4078              } else {              } else {
4079                !!!cp ('t128');                !!!cp ('t128');
4080                #                #
# Line 3642  sub _tree_construction_main ($) { Line 4084  sub _tree_construction_main ($) {
4084                !!!cp ('t129');                !!!cp ('t129');
4085                ## As if </noscript>                ## As if </noscript>
4086                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4087                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
4088                                
4089                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4090                ## As if </head>                ## As if </head>
# Line 3661  sub _tree_construction_main ($) { Line 4103  sub _tree_construction_main ($) {
4103    
4104              ## "after head" insertion mode              ## "after head" insertion mode
4105              ## As if <body>              ## As if <body>
4106              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4107              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4108              ## reprocess              ## reprocess
4109              redo B;              !!!ack-later;
4110                next B;
4111            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4112              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4113                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4114                  !!!cp ('t132');                  !!!cp ('t132');
4115                  ## As if <head>                  ## As if <head>
4116                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4117                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4118                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4119                        [$self->{head_element}, $el_category->{head}];
4120    
4121                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4122                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4123                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4124                  !!!next-token;                  !!!next-token;
4125                  redo B;                  next B;
4126                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4127                  !!!cp ('t133');                  !!!cp ('t133');
4128                  ## As if </noscript>                  ## As if </noscript>
4129                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4130                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/head', token => $token);
4131                                    
4132                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4133                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4134                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4135                  !!!next-token;                  !!!next-token;
4136                  redo B;                  next B;
4137                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4138                  !!!cp ('t134');                  !!!cp ('t134');
4139                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4140                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4141                  !!!next-token;                  !!!next-token;
4142                  redo B;                  next B;
4143                } else {                } else {
4144                  !!!cp ('t135');                  !!!cp ('t135');
4145                  #                  #
# Line 3706  sub _tree_construction_main ($) { Line 4150  sub _tree_construction_main ($) {
4150                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4151                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4152                  !!!next-token;                  !!!next-token;
4153                  redo B;                  next B;
4154                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4155                  !!!cp ('t137');                  !!!cp ('t137');
4156                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);
4157                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4158                  !!!next-token;                  !!!next-token;
4159                  redo B;                  next B;
4160                } else {                } else {
4161                  !!!cp ('t138');                  !!!cp ('t138');
4162                  #                  #
# Line 3723  sub _tree_construction_main ($) { Line 4167  sub _tree_construction_main ($) {
4167                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4168                  !!!cp ('t139');                  !!!cp ('t139');
4169                  ## As if <head>                  ## As if <head>
4170                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4171                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4172                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4173                        [$self->{head_element}, $el_category->{head}];
4174    
4175                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4176                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4177                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4178                  !!!cp ('t140');                  !!!cp ('t140');
4179                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4180                  ## Ignore the token                  ## Ignore the token
4181                  !!!next-token;                  !!!next-token;
4182                  redo B;                  next B;
4183                } else {                } else {
4184                  !!!cp ('t141');                  !!!cp ('t141');
4185                }                }
# Line 3746  sub _tree_construction_main ($) { Line 4191  sub _tree_construction_main ($) {
4191                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4192                  !!!cp ('t142');                  !!!cp ('t142');
4193                  ## As if <head>                  ## As if <head>
4194                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4195                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4196                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4197                        [$self->{head_element}, $el_category->{head}];
4198    
4199                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4200                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3763  sub _tree_construction_main ($) { Line 4209  sub _tree_construction_main ($) {
4209                  #                  #
4210                } else {                } else {
4211                  !!!cp ('t145');                  !!!cp ('t145');
4212                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4213                  ## Ignore the token                  ## Ignore the token
4214                  !!!next-token;                  !!!next-token;
4215                  redo B;                  next B;
4216                }                }
4217              }              }
4218    
# Line 3774  sub _tree_construction_main ($) { Line 4220  sub _tree_construction_main ($) {
4220                !!!cp ('t146');                !!!cp ('t146');
4221                ## As if </noscript>                ## As if </noscript>
4222                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4223                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);
4224                                
4225                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4226                ## As if </head>                ## As if </head>
# Line 3790  sub _tree_construction_main ($) { Line 4236  sub _tree_construction_main ($) {
4236              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4237  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
4238                !!!cp ('t148');                !!!cp ('t148');
4239                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4240                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
4241                !!!next-token;                !!!next-token;
4242                redo B;                next B;
4243              } else {              } else {
4244                !!!cp ('t149');                !!!cp ('t149');
4245              }              }
4246    
4247              ## "after head" insertion mode              ## "after head" insertion mode
4248              ## As if <body>              ## As if <body>
4249              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4250              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4251              ## reprocess              ## reprocess
4252              redo B;              next B;
4253            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4254              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4255            }            !!!cp ('t149.1');
4256    
4257              ## NOTE: As if <head>
4258              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4259              $self->{open_elements}->[-1]->[0]->append_child
4260                  ($self->{head_element});
4261              #push @{$self->{open_elements}},
4262              #    [$self->{head_element}, $el_category->{head}];
4263              #$self->{insertion_mode} = IN_HEAD_IM;
4264              ## NOTE: Reprocess.
4265    
4266              ## NOTE: As if </head>
4267              #pop @{$self->{open_elements}};
4268              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4269              ## NOTE: Reprocess.
4270              
4271              #
4272            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4273              !!!cp ('t149.2');
4274    
4275              ## NOTE: As if </head>
4276              pop @{$self->{open_elements}};
4277              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4278              ## NOTE: Reprocess.
4279    
4280              #
4281            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4282              !!!cp ('t149.3');
4283    
4284              !!!parse-error (type => 'in noscript:#eof', token => $token);
4285    
4286              ## As if </noscript>
4287              pop @{$self->{open_elements}};
4288              #$self->{insertion_mode} = IN_HEAD_IM;
4289              ## NOTE: Reprocess.
4290    
4291              ## NOTE: As if </head>
4292              pop @{$self->{open_elements}};
4293              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4294              ## NOTE: Reprocess.
4295    
4296              #
4297            } else {
4298              !!!cp ('t149.4');
4299              #
4300            }
4301    
4302            ## NOTE: As if <body>
4303            !!!insert-element ('body',, $token);
4304            $self->{insertion_mode} = IN_BODY_IM;
4305            ## NOTE: Reprocess.
4306            next B;
4307          } else {
4308            die "$0: $token->{type}: Unknown token type";
4309          }
4310    
4311            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
4312      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
# Line 3818  sub _tree_construction_main ($) { Line 4318  sub _tree_construction_main ($) {
4318              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4319    
4320              !!!next-token;              !!!next-token;
4321              redo B;              next B;
4322            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
4323              if ({              if ({
4324                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3826  sub _tree_construction_main ($) { Line 4326  sub _tree_construction_main ($) {
4326                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4327                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
4328                  ## have an element in table scope                  ## have an element in table scope
4329                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
4330                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4331                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
4332                      !!!cp ('t151');                      !!!cp ('t151');
4333                      $tn = $node->[1];  
4334                      last INSCOPE;                      ## Close the cell
4335                    } elsif ({                      !!!back-token; # <x>
4336                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
4337                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
4338                                  line => $token->{line},
4339                                  column => $token->{column}};
4340                        next B;
4341                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4342                      !!!cp ('t152');                      !!!cp ('t152');
4343                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
4344                        last;
4345                    }                    }
4346                  } # INSCOPE                  }
4347                    unless (defined $tn) {  
4348                      !!!cp ('t153');                  !!!cp ('t153');
4349  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
4350                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      value => $token->{tag_name}, token => $token);
4351                      ## Ignore the token                  ## Ignore the token
4352                      !!!next-token;                  !!!nack ('t153.1');
4353                      redo B;                  !!!next-token;
4354                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo B;  
4355                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4356                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed:caption', token => $token);
4357                                    
4358                  ## As if </caption>                  ## NOTE: As if </caption>.
4359                  ## have a table element in table scope                  ## have a table element in table scope
4360                  my $i;                  my $i;
4361                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
4362                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
4363                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
4364                      !!!cp ('t155');                      if ($node->[1] & CAPTION_EL) {
4365                      $i = $_;                        !!!cp ('t155');
4366                      last INSCOPE;                        $i = $_;
4367                    } elsif ({                        last INSCOPE;
4368                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4369                             }->{$node->[1]}) {                        !!!cp ('t156');
4370                      !!!cp ('t156');                        last;
4371                      last INSCOPE;                      }
4372                    }                    }
4373    
4374                      !!!cp ('t157');
4375                      !!!parse-error (type => 'start tag not allowed',
4376                                      value => $token->{tag_name}, token => $token);
4377                      ## Ignore the token
4378                      !!!nack ('t157.1');
4379                      !!!next-token;
4380                      next B;
4381                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t157');  
 ## TODO: this type is wrong.  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
4382                                    
4383                  ## generate implied end tags                  ## generate implied end tags
4384                  while ({                  while ($self->{open_elements}->[-1]->[1]
4385                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
4386                    !!!cp ('t158');                    !!!cp ('t158');
4387                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4388                  }                  }
4389    
4390                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4391                    !!!cp ('t159');                    !!!cp ('t159');
4392                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
4393                                      value => $self->{open_elements}->[-1]->[0]
4394                                          ->manakai_local_name,
4395                                      token => $token);
4396                  } else {                  } else {
4397                    !!!cp ('t160');                    !!!cp ('t160');
4398                  }                  }
# Line 3904  sub _tree_construction_main ($) { Line 4404  sub _tree_construction_main ($) {
4404                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
4405                                    
4406                  ## reprocess                  ## reprocess
4407                  redo B;                  !!!ack-later;
4408                    next B;
4409                } else {                } else {
4410                  !!!cp ('t161');                  !!!cp ('t161');
4411                  #                  #
# Line 3920  sub _tree_construction_main ($) { Line 4421  sub _tree_construction_main ($) {
4421                  my $i;                  my $i;
4422                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4423                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4424                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4425                      !!!cp ('t163');                      !!!cp ('t163');
4426                      $i = $_;                      $i = $_;
4427                      last INSCOPE;                      last INSCOPE;
4428                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
4429                      !!!cp ('t164');                      !!!cp ('t164');
4430                      last INSCOPE;                      last INSCOPE;
4431                    }                    }
4432                  } # INSCOPE                  } # INSCOPE
4433                    unless (defined $i) {                    unless (defined $i) {
4434                      !!!cp ('t165');                      !!!cp ('t165');
4435                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4436                      ## Ignore the token                      ## Ignore the token
4437                      !!!next-token;                      !!!next-token;
4438                      redo B;                      next B;
4439                    }                    }
4440                                    
4441                  ## generate implied end tags                  ## generate implied end tags
4442                  while ({                  while ($self->{open_elements}->[-1]->[1]
4443                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
4444                    !!!cp ('t166');                    !!!cp ('t166');
4445                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4446                  }                  }
4447    
4448                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
4449                            ne $token->{tag_name}) {
4450                    !!!cp ('t167');                    !!!cp ('t167');
4451                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
4452                                      value => $self->{open_elements}->[-1]->[0]
4453                                          ->manakai_local_name,
4454                                      token => $token);
4455                  } else {                  } else {
4456                    !!!cp ('t168');                    !!!cp ('t168');
4457                  }                  }
# Line 3961  sub _tree_construction_main ($) { Line 4463  sub _tree_construction_main ($) {
4463                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
4464                                    
4465                  !!!next-token;                  !!!next-token;
4466                  redo B;                  next B;
4467                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4468                  !!!cp ('t169');                  !!!cp ('t169');
4469                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4470                  ## Ignore the token                  ## Ignore the token
4471                  !!!next-token;                  !!!next-token;
4472                  redo B;                  next B;
4473                } else {                } else {
4474                  !!!cp ('t170');                  !!!cp ('t170');
4475                  #                  #
# Line 3976  sub _tree_construction_main ($) { Line 4478  sub _tree_construction_main ($) {
4478                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
4479                  ## have a table element in table scope                  ## have a table element in table scope
4480                  my $i;                  my $i;
4481                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
4482                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
4483                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
4484                      !!!cp ('t171');                      if ($node->[1] & CAPTION_EL) {
4485                      $i = $_;                        !!!cp ('t171');
4486                      last INSCOPE;                        $i = $_;
4487                    } elsif ({                        last INSCOPE;
4488                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4489                             }->{$node->[1]}) {                        !!!cp ('t172');
4490                      !!!cp ('t172');                        last;
4491                      last INSCOPE;                      }
4492                    }                    }
4493    
4494                      !!!cp ('t173');
4495                      !!!parse-error (type => 'unmatched end tag',
4496                                      value => $token->{tag_name}, token => $token);
4497                      ## Ignore the token
4498                      !!!next-token;
4499                      next B;
4500                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
4501                                    
4502                  ## generate implied end tags                  ## generate implied end tags
4503                  while ({                  while ($self->{open_elements}->[-1]->[1]
4504                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
4505                    !!!cp ('t174');                    !!!cp ('t174');
4506                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4507                  }                  }
4508                                    
4509                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4510                    !!!cp ('t175');                    !!!cp ('t175');
4511                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
4512                                      value => $self->{open_elements}->[-1]->[0]
4513                                          ->manakai_local_name,
4514                                      token => $token);
4515                  } else {                  } else {
4516                    !!!cp ('t176');                    !!!cp ('t176');
4517                  }                  }
# Line 4019  sub _tree_construction_main ($) { Line 4523  sub _tree_construction_main ($) {
4523                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
4524                                    
4525                  !!!next-token;                  !!!next-token;
4526                  redo B;                  next B;
4527                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
4528                  !!!cp ('t177');                  !!!cp ('t177');
4529                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4530                  ## Ignore the token                  ## Ignore the token
4531                  !!!next-token;                  !!!next-token;
4532                  redo B;                  next B;
4533                } else {                } else {
4534                  !!!cp ('t178');                  !!!cp ('t178');
4535                  #                  #
# Line 4038  sub _tree_construction_main ($) { Line 4542  sub _tree_construction_main ($) {
4542                ## have an element in table scope                ## have an element in table scope
4543                my $i;                my $i;
4544                my $tn;                my $tn;
4545                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
4546                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
4547                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
4548                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
4549                    $i = $_;                      !!!cp ('t179');
4550                    last INSCOPE;                      $i = $_;
4551                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
4552                    !!!cp ('t180');                      ## Close the cell
4553                    $tn = $node->[1];                      !!!back-token; # </x>
4554                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
4555                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
4556                  } elsif ({                                column => $token->{column}};
4557                            table => 1, html => 1,                      next B;
4558                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_CELL_EL) {
4559                    !!!cp ('t181');                      !!!cp ('t180');
4560                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
4561                        ## NOTE: There is exactly one |td| or |th| element
4562                        ## in scope in the stack of open elements by definition.
4563                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4564                        ## ISSUE: Can this be reached?
4565                        !!!cp ('t181');
4566                        last;
4567                      }
4568                  }                  }
4569                } # INSCOPE  
               unless (defined $i) {  
4570                  !!!cp ('t182');                  !!!cp ('t182');
4571                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
4572                        value => $token->{tag_name}, token => $token);
4573                  ## Ignore the token                  ## Ignore the token
4574                  !!!next-token;                  !!!next-token;
4575                  redo B;                  next B;
4576                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
4577              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
4578                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
4579                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed:caption', token => $token);
4580    
4581                ## As if </caption>                ## As if </caption>
4582                ## have a table element in table scope                ## have a table element in table scope
4583                my $i;                my $i;
4584                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4585                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4586                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
4587                    !!!cp ('t184');                    !!!cp ('t184');
4588                    $i = $_;                    $i = $_;
4589                    last INSCOPE;                    last INSCOPE;
4590                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
4591                    !!!cp ('t185');                    !!!cp ('t185');
4592                    last INSCOPE;                    last INSCOPE;
4593                  }                  }
4594                } # INSCOPE                } # INSCOPE
4595                unless (defined $i) {                unless (defined $i) {
4596                  !!!cp ('t186');                  !!!cp ('t186');
4597                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);
4598                  ## Ignore the token                  ## Ignore the token
4599                  !!!next-token;                  !!!next-token;
4600                  redo B;                  next B;
4601                }                }
4602                                
4603                ## generate implied end tags                ## generate implied end tags
4604                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
4605                  !!!cp ('t187');                  !!!cp ('t187');
4606                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4607                }                }
4608    
4609                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4610                  !!!cp ('t188');                  !!!cp ('t188');
4611                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
4612                                    value => $self->{open_elements}->[-1]->[0]
4613                                        ->manakai_local_name,
4614                                    token => $token);
4615                } else {                } else {
4616                  !!!cp ('t189');                  !!!cp ('t189');
4617                }                }
# Line 4120  sub _tree_construction_main ($) { Line 4623  sub _tree_construction_main ($) {
4623                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
4624    
4625                ## reprocess                ## reprocess
4626                redo B;                next B;
4627              } elsif ({              } elsif ({
4628                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
4629                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4630                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
4631                  !!!cp ('t190');                  !!!cp ('t190');
4632                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4633                  ## Ignore the token                  ## Ignore the token
4634                  !!!next-token;                  !!!next-token;
4635                  redo B;                  next B;
4636                } else {                } else {
4637                  !!!cp ('t191');                  !!!cp ('t191');
4638                  #                  #
# Line 4140  sub _tree_construction_main ($) { Line 4643  sub _tree_construction_main ($) {
4643                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
4644                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
4645                !!!cp ('t192');                !!!cp ('t192');
4646                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4647                ## Ignore the token                ## Ignore the token
4648                !!!next-token;                !!!next-token;
4649                redo B;                next B;
4650              } else {              } else {
4651                !!!cp ('t193');                !!!cp ('t193');
4652                #                #
4653              }              }
4654          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4655            for my $entry (@{$self->{open_elements}}) {
4656              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
4657                !!!cp ('t75');
4658                !!!parse-error (type => 'in body:#eof', token => $token);
4659                last;
4660              }
4661            }
4662    
4663            ## Stop parsing.
4664            last B;
4665        } else {        } else {
4666          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4667        }        }
# Line 4156  sub _tree_construction_main ($) { Line 4670  sub _tree_construction_main ($) {
4670        #        #
4671      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
4672        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4673              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
4674                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4675              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4676                                
4677                unless (length $token->{data}) {            unless (length $token->{data}) {
4678                  !!!cp ('t194');              !!!cp ('t194');
4679                  !!!next-token;              !!!next-token;
4680                  redo B;              next B;
4681                } else {            } else {
4682                  !!!cp ('t195');              !!!cp ('t195');
4683                }            }
4684              }          }
4685    
4686              !!!parse-error (type => 'in table:#character');              !!!parse-error (type => 'in table:#character', token => $token);
4687    
4688              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
4689              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4176  sub _tree_construction_main ($) { Line 4691  sub _tree_construction_main ($) {
4691              ## result in a new Text node.              ## result in a new Text node.
4692              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
4693                            
4694              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]}) {  
4695                # MUST                # MUST
4696                my $foster_parent_element;                my $foster_parent_element;
4697                my $next_sibling;                my $next_sibling;
4698                my $prev_sibling;                my $prev_sibling;
4699                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
4700                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4701                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4702                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
4703                      !!!cp ('t196');                      !!!cp ('t196');
# Line 4213  sub _tree_construction_main ($) { Line 4725  sub _tree_construction_main ($) {
4725                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
4726                     $next_sibling);                     $next_sibling);
4727                }                }
4728              } else {            $open_tables->[-1]->[1] = 1; # tainted
4729                !!!cp ('t200');          } else {
4730                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            !!!cp ('t200');
4731              }            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4732            }
4733                            
4734              !!!next-token;          !!!next-token;
4735              redo B;          next B;
4736        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4737              if ({              if ({
4738                   tr => ($self->{insertion_mode} != IN_ROW_IM),                   tr => ($self->{insertion_mode} != IN_ROW_IM),
# Line 4227  sub _tree_construction_main ($) { Line 4740  sub _tree_construction_main ($) {
4740                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4741                if ($self->{insertion_mode} == IN_TABLE_IM) {                if ($self->{insertion_mode} == IN_TABLE_IM) {
4742                  ## Clear back to table context                  ## Clear back to table context
4743                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4744                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4745                    !!!cp ('t201');                    !!!cp ('t201');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4746                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4747                  }                  }
4748                                    
4749                  !!!insert-element ('tbody');                  !!!insert-element ('tbody',, $token);
4750                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
4751                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
4752                }                }
# Line 4242  sub _tree_construction_main ($) { Line 4754  sub _tree_construction_main ($) {
4754                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
4755                  unless ($token->{tag_name} eq 'tr') {                  unless ($token->{tag_name} eq 'tr') {
4756                    !!!cp ('t202');                    !!!cp ('t202');
4757                    !!!parse-error (type => 'missing start tag:tr');                    !!!parse-error (type => 'missing start tag:tr', token => $token);
4758                  }                  }
4759                                    
4760                  ## Clear back to table body context                  ## Clear back to table body context
4761                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4762                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
4763                    !!!cp ('t203');                    !!!cp ('t203');
4764                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4765                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4766                  }                  }
4767                                    
4768                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
4769                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
4770                    !!!cp ('t204');                    !!!cp ('t204');
4771                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4772                      !!!nack ('t204');
4773                    !!!next-token;                    !!!next-token;
4774                    redo B;                    next B;
4775                  } else {                  } else {
4776                    !!!cp ('t205');                    !!!cp ('t205');
4777                    !!!insert-element ('tr');                    !!!insert-element ('tr',, $token);
4778                    ## reprocess in the "in row" insertion mode                    ## reprocess in the "in row" insertion mode
4779                  }                  }
4780                } else {                } else {
# Line 4271  sub _tree_construction_main ($) { Line 4782  sub _tree_construction_main ($) {
4782                }                }
4783    
4784                ## Clear back to table row context                ## Clear back to table row context
4785                while (not {                while (not ($self->{open_elements}->[-1]->[1]
4786                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
4787                  !!!cp ('t207');                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4788                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4789                }                }
4790                                
4791                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4792                $self->{insertion_mode} = IN_CELL_IM;                $self->{insertion_mode} = IN_CELL_IM;
4793    
4794                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
4795                                
4796                  !!!nack ('t207.1');
4797                !!!next-token;                !!!next-token;
4798                redo B;                next B;
4799              } elsif ({              } elsif ({
4800                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
4801                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 4297  sub _tree_construction_main ($) { Line 4807  sub _tree_construction_main ($) {
4807                  my $i;                  my $i;
4808                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4809                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4810                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
4811                      !!!cp ('t208');                      !!!cp ('t208');
4812                      $i = $_;                      $i = $_;
4813                      last INSCOPE;                      last INSCOPE;
4814                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
4815                      !!!cp ('t209');                      !!!cp ('t209');
4816                      last INSCOPE;                      last INSCOPE;
4817                    }                    }
4818                  } # INSCOPE                  } # INSCOPE
4819                  unless (defined $i) {                  unless (defined $i) {
4820                   !!!cp ('t210');                    !!!cp ('t210');
4821  ## TODO: This type is wrong.  ## TODO: This type is wrong.
4822                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);
4823                    ## Ignore the token                    ## Ignore the token
4824                      !!!nack ('t210.1');
4825                    !!!next-token;                    !!!next-token;
4826                    redo B;                    next B;
4827                  }                  }
4828                                    
4829                  ## Clear back to table row context                  ## Clear back to table row context
4830                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4831                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
4832                    !!!cp ('t211');                    !!!cp ('t211');
4833                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4834                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4835                  }                  }
4836                                    
# Line 4335  sub _tree_construction_main ($) { Line 4839  sub _tree_construction_main ($) {
4839                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
4840                    !!!cp ('t212');                    !!!cp ('t212');
4841                    ## reprocess                    ## reprocess
4842                    redo B;                    !!!ack-later;
4843                      next B;
4844                  } else {                  } else {
4845                    !!!cp ('t213');                    !!!cp ('t213');
4846                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4347  sub _tree_construction_main ($) { Line 4852  sub _tree_construction_main ($) {
4852                  my $i;                  my $i;
4853                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4854                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
4855                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
4856                      !!!cp ('t214');                      !!!cp ('t214');
4857                      $i = $_;                      $i = $_;
4858                      last INSCOPE;                      last INSCOPE;
4859                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
4860                      !!!cp ('t215');                      !!!cp ('t215');
4861                      last INSCOPE;                      last INSCOPE;
4862                    }                    }
# Line 4363  sub _tree_construction_main ($) { Line 4864  sub _tree_construction_main ($) {
4864                  unless (defined $i) {                  unless (defined $i) {
4865                    !!!cp ('t216');                    !!!cp ('t216');
4866  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type ios wrong.
4867                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
4868                    ## Ignore the token                    ## Ignore the token
4869                      !!!nack ('t216.1');
4870                    !!!next-token;                    !!!next-token;
4871                    redo B;                    next B;
4872                  }                  }
4873    
4874                  ## Clear back to table body context                  ## Clear back to table body context
4875                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
4876                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
4877                    !!!cp ('t217');                    !!!cp ('t217');
4878                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4879                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4880                  }                  }
4881                                    
# Line 4395  sub _tree_construction_main ($) { Line 4895  sub _tree_construction_main ($) {
4895    
4896                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
4897                  ## Clear back to table context                  ## Clear back to table context
4898                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4899                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4900                    !!!cp ('t219');                    !!!cp ('t219');
4901                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4902                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4903                  }                  }
4904                                    
4905                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup',, $token);
4906                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
4907                  ## reprocess                  ## reprocess
4908                  redo B;                  !!!ack-later;
4909                    next B;
4910                } elsif ({                } elsif ({
4911                          caption => 1,                          caption => 1,
4912                          colgroup => 1,                          colgroup => 1,
4913                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
4914                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
4915                  ## Clear back to table context                  ## Clear back to table context
4916                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
4917                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
4918                    !!!cp ('t220');                    !!!cp ('t220');
4919                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4920                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
4921                  }                  }
4922                                    
4923                  push @$active_formatting_elements, ['#marker', '']                  push @$active_formatting_elements, ['#marker', '']
4924                      if $token->{tag_name} eq 'caption';                      if $token->{tag_name} eq 'caption';
4925                                    
4926                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4927                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
4928                                             caption => IN_CAPTION_IM,                                             caption => IN_CAPTION_IM,
4929                                             colgroup => IN_COLUMN_GROUP_IM,                                             colgroup => IN_COLUMN_GROUP_IM,
# Line 4433  sub _tree_construction_main ($) { Line 4932  sub _tree_construction_main ($) {
4932                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
4933                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
4934                  !!!next-token;                  !!!next-token;
4935                  redo B;                  !!!nack ('t220.1');
4936                    next B;
4937                } else {                } else {
4938                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
4939                }                }
4940              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4941                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
4942                                  value => $self->{open_elements}->[-1]->[0]
4943                                      ->manakai_local_name,
4944                                  token => $token);
4945    
4946                ## As if </table>                ## As if </table>
4947                ## have a table element in table scope                ## have a table element in table scope
4948                my $i;                my $i;
4949                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4950                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
4951                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
4952                    !!!cp ('t221');                    !!!cp ('t221');
4953                    $i = $_;                    $i = $_;
4954                    last INSCOPE;                    last INSCOPE;
4955                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
4956                    !!!cp ('t222');                    !!!cp ('t222');
4957                    last INSCOPE;                    last INSCOPE;
4958                  }                  }
# Line 4460  sub _tree_construction_main ($) { Line 4960  sub _tree_construction_main ($) {
4960                unless (defined $i) {                unless (defined $i) {
4961                  !!!cp ('t223');                  !!!cp ('t223');
4962  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
4963                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag:table', token => $token);
4964                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
4965                    !!!nack ('t223.1');
4966                  !!!next-token;                  !!!next-token;
4967                  redo B;                  next B;
4968                }                }
4969                                
4970    ## TODO: Followings are removed from the latest spec.
4971                ## generate implied end tags                ## generate implied end tags
4972                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
4973                  !!!cp ('t224');                  !!!cp ('t224');
4974                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4975                }                }
4976    
4977                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
4978                  !!!cp ('t225');                  !!!cp ('t225');
4979  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
4980                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
4981                                    value => $self->{open_elements}->[-1]->[0]
4982                                        ->manakai_local_name,
4983                                    token => $token);
4984                } else {                } else {
4985                  !!!cp ('t226');                  !!!cp ('t226');
4986                }                }
4987    
4988                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4989                  pop @{$open_tables};
4990    
4991                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
4992    
4993                ## reprocess            ## reprocess
4994                redo B;            !!!ack-later;
4995              next B;
4996            } elsif ($token->{tag_name} eq 'style') {
4997              if (not $open_tables->[-1]->[1]) { # tainted
4998                !!!cp ('t227.8');
4999                ## NOTE: This is a "as if in head" code clone.
5000                $parse_rcdata->(CDATA_CONTENT_MODEL);
5001                next B;
5002              } else {
5003                !!!cp ('t227.7');
5004                #
5005              }
5006            } elsif ($token->{tag_name} eq 'script') {
5007              if (not $open_tables->[-1]->[1]) { # tainted
5008                !!!cp ('t227.6');
5009                ## NOTE: This is a "as if in head" code clone.
5010                $script_start_tag->();
5011                next B;
5012              } else {
5013                !!!cp ('t227.5');
5014                #
5015              }
5016            } elsif ($token->{tag_name} eq 'input') {
5017              if (not $open_tables->[-1]->[1]) { # tainted
5018                if ($token->{attributes}->{type}) { ## TODO: case
5019                  my $type = lc $token->{attributes}->{type}->{value};
5020                  if ($type eq 'hidden') {
5021                    !!!cp ('t227.3');
5022                    !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
5023    
5024                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5025    
5026                    ## TODO: form element pointer
5027    
5028                    pop @{$self->{open_elements}};
5029    
5030                    !!!next-token;
5031                    !!!ack ('t227.2.1');
5032                    next B;
5033                  } else {
5034                    !!!cp ('t227.2');
5035                    #
5036                  }
5037                } else {
5038                  !!!cp ('t227.1');
5039                  #
5040                }
5041              } else {
5042                !!!cp ('t227.4');
5043                #
5044              }
5045          } else {          } else {
5046            !!!cp ('t227');            !!!cp ('t227');
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
   
           $insert = $insert_to_foster;  
5047            #            #
5048          }          }
5049    
5050            !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);
5051    
5052            $insert = $insert_to_foster;
5053            #
5054        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5055              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
5056                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 4502  sub _tree_construction_main ($) { Line 5058  sub _tree_construction_main ($) {
5058                my $i;                my $i;
5059                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5060                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5061                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5062                    !!!cp ('t228');                    !!!cp ('t228');
5063                    $i = $_;                    $i = $_;
5064                    last INSCOPE;                    last INSCOPE;
5065                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5066                    !!!cp ('t229');                    !!!cp ('t229');
5067                    last INSCOPE;                    last INSCOPE;
5068                  }                  }
5069                } # INSCOPE                } # INSCOPE
5070                unless (defined $i) {                unless (defined $i) {
5071                  !!!cp ('t230');                  !!!cp ('t230');
5072                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5073                  ## Ignore the token                  ## Ignore the token
5074                    !!!nack ('t230.1');
5075                  !!!next-token;                  !!!next-token;
5076                  redo B;                  next B;
5077                } else {                } else {
5078                  !!!cp ('t232');                  !!!cp ('t232');
5079                }                }
5080    
5081                ## Clear back to table row context                ## Clear back to table row context
5082                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5083                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5084                  !!!cp ('t231');                  !!!cp ('t231');
5085  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5086                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5087                }                }
5088    
5089                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5090                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5091                !!!next-token;                !!!next-token;
5092                redo B;                !!!nack ('t231.1');
5093                  next B;
5094              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5095                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5096                  ## As if </tr>                  ## As if </tr>
# Line 4544  sub _tree_construction_main ($) { Line 5098  sub _tree_construction_main ($) {
5098                  my $i;                  my $i;
5099                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5100                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5101                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5102                      !!!cp ('t233');                      !!!cp ('t233');
5103                      $i = $_;                      $i = $_;
5104                      last INSCOPE;                      last INSCOPE;
5105                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5106                      !!!cp ('t234');                      !!!cp ('t234');
5107                      last INSCOPE;                      last INSCOPE;
5108                    }                    }
# Line 4558  sub _tree_construction_main ($) { Line 5110  sub _tree_construction_main ($) {
5110                  unless (defined $i) {                  unless (defined $i) {
5111                    !!!cp ('t235');                    !!!cp ('t235');
5112  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5113                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);
5114                    ## Ignore the token                    ## Ignore the token
5115                      !!!nack ('t236.1');
5116                    !!!next-token;                    !!!next-token;
5117                    redo B;                    next B;
5118                  }                  }
5119                                    
5120                  ## Clear back to table row context                  ## Clear back to table row context
5121                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5122                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5123                    !!!cp ('t236');                    !!!cp ('t236');
5124  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5125                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5126                  }                  }
5127                                    
# Line 4584  sub _tree_construction_main ($) { Line 5135  sub _tree_construction_main ($) {
5135                  my $i;                  my $i;
5136                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5137                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5138                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5139                      !!!cp ('t237');                      !!!cp ('t237');
5140                      $i = $_;                      $i = $_;
5141                      last INSCOPE;                      last INSCOPE;
5142                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5143                      !!!cp ('t238');                      !!!cp ('t238');
5144                      last INSCOPE;                      last INSCOPE;
5145                    }                    }
5146                  } # INSCOPE                  } # INSCOPE
5147                  unless (defined $i) {                  unless (defined $i) {
5148                    !!!cp ('t239');                    !!!cp ('t239');
5149                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5150                    ## Ignore the token                    ## Ignore the token
5151                      !!!nack ('t239.1');
5152                    !!!next-token;                    !!!next-token;
5153                    redo B;                    next B;
5154                  }                  }
5155                                    
5156                  ## Clear back to table body context                  ## Clear back to table body context
5157                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5158                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5159                    !!!cp ('t240');                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5160                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5161                  }                  }
5162                                    
# Line 4626  sub _tree_construction_main ($) { Line 5172  sub _tree_construction_main ($) {
5172                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
5173                }                }
5174    
5175                  ## NOTE: </table> in the "in table" insertion mode.
5176                  ## When you edit the code fragment below, please ensure that
5177                  ## the code for <table> in the "in table" insertion mode
5178                  ## is synced with it.
5179    
5180                ## have a table element in table scope                ## have a table element in table scope
5181                my $i;                my $i;
5182                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5183                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5184                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
5185                    !!!cp ('t241');                    !!!cp ('t241');
5186                    $i = $_;                    $i = $_;
5187                    last INSCOPE;                    last INSCOPE;
5188                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5189                    !!!cp ('t242');                    !!!cp ('t242');
5190                    last INSCOPE;                    last INSCOPE;
5191                  }                  }
5192                } # INSCOPE                } # INSCOPE
5193                unless (defined $i) {                unless (defined $i) {
5194                  !!!cp ('t243');                  !!!cp ('t243');
5195                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5196                  ## Ignore the token                  ## Ignore the token
5197                    !!!nack ('t243.1');
5198                  !!!next-token;                  !!!next-token;
5199                  redo B;                  next B;
               }  
   
               ## generate implied end tags  
               while ({  
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!cp ('t244');  
 ## ISSUE: Can this case be reached?  
                 pop @{$self->{open_elements}};  
               }  
                 
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!cp ('t245');  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               } else {  
                 !!!cp ('t246');  
5200                }                }
5201                                    
5202                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5203                  pop @{$open_tables};
5204                                
5205                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5206                                
5207                !!!next-token;                !!!next-token;
5208                redo B;                next B;
5209              } elsif ({              } elsif ({
5210                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5211                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4680  sub _tree_construction_main ($) { Line 5215  sub _tree_construction_main ($) {
5215                  my $i;                  my $i;
5216                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5217                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5218                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5219                      !!!cp ('t247');                      !!!cp ('t247');
5220                      $i = $_;                      $i = $_;
5221                      last INSCOPE;                      last INSCOPE;
5222                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5223                      !!!cp ('t248');                      !!!cp ('t248');
5224                      last INSCOPE;                      last INSCOPE;
5225                    }                    }
5226                  } # INSCOPE                  } # INSCOPE
5227                    unless (defined $i) {                    unless (defined $i) {
5228                      !!!cp ('t249');                      !!!cp ('t249');
5229                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5230                      ## Ignore the token                      ## Ignore the token
5231                        !!!nack ('t249.1');
5232                      !!!next-token;                      !!!next-token;
5233                      redo B;                      next B;
5234                    }                    }
5235                                    
5236                  ## As if </tr>                  ## As if </tr>
# Line 4704  sub _tree_construction_main ($) { Line 5238  sub _tree_construction_main ($) {
5238                  my $i;                  my $i;
5239                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5240                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5241                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5242                      !!!cp ('t250');                      !!!cp ('t250');
5243                      $i = $_;                      $i = $_;
5244                      last INSCOPE;                      last INSCOPE;
5245                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5246                      !!!cp ('t251');                      !!!cp ('t251');
5247                      last INSCOPE;                      last INSCOPE;
5248                    }                    }
5249                  } # INSCOPE                  } # INSCOPE
5250                    unless (defined $i) {                    unless (defined $i) {
5251                      !!!cp ('t252');                      !!!cp ('t252');
5252                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);
5253                      ## Ignore the token                      ## Ignore the token
5254                        !!!nack ('t252.1');
5255                      !!!next-token;                      !!!next-token;
5256                      redo B;                      next B;
5257                    }                    }
5258                                    
5259                  ## Clear back to table row context                  ## Clear back to table row context
5260                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5261                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5262                    !!!cp ('t253');                    !!!cp ('t253');
5263  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5264                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5265                  }                  }
5266                                    
# Line 4742  sub _tree_construction_main ($) { Line 5273  sub _tree_construction_main ($) {
5273                my $i;                my $i;
5274                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5275                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5276                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5277                    !!!cp ('t254');                    !!!cp ('t254');
5278                    $i = $_;                    $i = $_;
5279                    last INSCOPE;                    last INSCOPE;
5280                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5281                    !!!cp ('t255');                    !!!cp ('t255');
5282                    last INSCOPE;                    last INSCOPE;
5283                  }                  }
5284                } # INSCOPE                } # INSCOPE
5285                unless (defined $i) {                unless (defined $i) {
5286                  !!!cp ('t256');                  !!!cp ('t256');
5287                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5288                  ## Ignore the token                  ## Ignore the token
5289                    !!!nack ('t256.1');
5290                  !!!next-token;                  !!!next-token;
5291                  redo B;                  next B;
5292                }                }
5293    
5294                ## Clear back to table body context                ## Clear back to table body context
5295                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5296                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5297                  !!!cp ('t257');                  !!!cp ('t257');
5298  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5299                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5300                }                }
5301    
5302                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5303                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5304                  !!!nack ('t257.1');
5305                !!!next-token;                !!!next-token;
5306                redo B;                next B;
5307              } elsif ({              } elsif ({
5308                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
5309                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
5310                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5311                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
5312                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5313                !!!cp ('t258');            !!!cp ('t258');
5314                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5315                ## Ignore the token            ## Ignore the token
5316                !!!next-token;            !!!nack ('t258.1');
5317                redo B;             !!!next-token;
5318              next B;
5319          } else {          } else {
5320            !!!cp ('t259');            !!!cp ('t259');
5321            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);
5322    
5323            $insert = $insert_to_foster;            $insert = $insert_to_foster;
5324            #            #
5325          }          }
5326          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5327            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5328                    @{$self->{open_elements}} == 1) { # redundant, maybe
5329              !!!parse-error (type => 'in body:#eof', token => $token);
5330              !!!cp ('t259.1');
5331              #
5332            } else {
5333              !!!cp ('t259.2');
5334              #
5335            }
5336    
5337            ## Stop parsing
5338            last B;
5339        } else {        } else {
5340          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5341        }        }
# Line 4803  sub _tree_construction_main ($) { Line 5346  sub _tree_construction_main ($) {
5346                unless (length $token->{data}) {                unless (length $token->{data}) {
5347                  !!!cp ('t260');                  !!!cp ('t260');
5348                  !!!next-token;                  !!!next-token;
5349                  redo B;                  next B;
5350                }                }
5351              }              }
5352                            
# Line 4812  sub _tree_construction_main ($) { Line 5355  sub _tree_construction_main ($) {
5355            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5356              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
5357                !!!cp ('t262');                !!!cp ('t262');
5358                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5359                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5360                  !!!ack ('t262.1');
5361                !!!next-token;                !!!next-token;
5362                redo B;                next B;
5363              } else {              } else {
5364                !!!cp ('t263');                !!!cp ('t263');
5365                #                #
5366              }              }
5367            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
5368              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
5369                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5370                  !!!cp ('t264');                  !!!cp ('t264');
5371                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5372                  ## Ignore the token                  ## Ignore the token
5373                  !!!next-token;                  !!!next-token;
5374                  redo B;                  next B;
5375                } else {                } else {
5376                  !!!cp ('t265');                  !!!cp ('t265');
5377                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
5378                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5379                  !!!next-token;                  !!!next-token;
5380                  redo B;                              next B;            
5381                }                }
5382              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
5383                !!!cp ('t266');                !!!cp ('t266');
5384                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag:col', token => $token);
5385                ## Ignore the token                ## Ignore the token
5386                !!!next-token;                !!!next-token;
5387                redo B;                next B;
5388              } else {              } else {
5389                !!!cp ('t267');                !!!cp ('t267');
5390                #                #
5391              }              }
5392            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5393              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5394            }              @{$self->{open_elements}} == 1) { # redundant, maybe
5395              !!!cp ('t270.2');
5396              ## Stop parsing.
5397              last B;
5398            } else {
5399              ## NOTE: As if </colgroup>.
5400              !!!cp ('t270.1');
5401              pop @{$self->{open_elements}}; # colgroup
5402              $self->{insertion_mode} = IN_TABLE_IM;
5403              ## Reprocess.
5404              next B;
5405            }
5406          } else {
5407            die "$0: $token->{type}: Unknown token type";
5408          }
5409    
5410            ## As if </colgroup>            ## As if </colgroup>
5411            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5412              !!!cp ('t269');              !!!cp ('t269');
5413              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
5414                !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);
5415              ## Ignore the token              ## Ignore the token
5416                !!!nack ('t269.1');
5417              !!!next-token;              !!!next-token;
5418              redo B;              next B;
5419            } else {            } else {
5420              !!!cp ('t270');              !!!cp ('t270');
5421              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
5422              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
5423                !!!ack-later;
5424              ## reprocess              ## reprocess
5425              redo B;              next B;
5426            }            }
5427      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
5428        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5429          !!!cp ('t271');          !!!cp ('t271');
5430          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5431          !!!next-token;          !!!next-token;
5432          redo B;          next B;
5433        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5434              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
5435                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5436                  !!!cp ('t272');              !!!cp ('t272');
5437                  ## As if </option>              ## As if </option>
5438                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
5439                } else {            } else {
5440                  !!!cp ('t273');              !!!cp ('t273');
5441                }            }
5442    
5443                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5444                !!!next-token;            !!!nack ('t273.1');
5445                redo B;            !!!next-token;
5446              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
5447                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
5448                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5449                  ## As if </option>              !!!cp ('t274');
5450                  pop @{$self->{open_elements}};              ## As if </option>
5451                } else {              pop @{$self->{open_elements}};
5452                  !!!cp ('t275');            } else {
5453                }              !!!cp ('t275');
5454              }
5455    
5456                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5457                  !!!cp ('t276');              !!!cp ('t276');
5458                  ## As if </optgroup>              ## As if </optgroup>
5459                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
5460                } else {            } else {
5461                  !!!cp ('t277');              !!!cp ('t277');
5462                }            }
5463    
5464                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5465                !!!next-token;            !!!nack ('t277.1');
5466                redo B;            !!!next-token;
5467              } elsif ($token->{tag_name} eq 'select') {            next B;
5468  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ($token->{tag_name} eq 'select' or
5469                !!!parse-error (type => 'not closed:select');                   $token->{tag_name} eq 'input' or
5470                ## As if </select> instead                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5471                ## have an element in table scope                    {
5472                my $i;                     caption => 1, table => 1,
5473                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     tbody => 1, tfoot => 1, thead => 1,
5474                  my $node = $self->{open_elements}->[$_];                     tr => 1, td => 1, th => 1,
5475                  if ($node->[1] eq $token->{tag_name}) {                    }->{$token->{tag_name}})) {
5476                    !!!cp ('t278');            ## TODO: The type below is not good - <select> is replaced by </select>
5477                    $i = $_;            !!!parse-error (type => 'not closed:select', token => $token);
5478                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
5479                  } elsif ({            ## as if there were </select> (otherwise).
5480                            table => 1, html => 1,            ## have an element in table scope
5481                           }->{$node->[1]}) {            my $i;
5482                    !!!cp ('t279');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5483                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
5484                  }              if ($node->[1] & SELECT_EL) {
5485                } # INSCOPE                !!!cp ('t278');
5486                unless (defined $i) {                $i = $_;
5487                  !!!cp ('t280');                last INSCOPE;
5488                  !!!parse-error (type => 'unmatched end tag:select');              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5489                  ## Ignore the token                !!!cp ('t279');
5490                  !!!next-token;                last INSCOPE;
5491                  redo B;              }
5492                }            } # INSCOPE
5493              unless (defined $i) {
5494                !!!cp ('t280');
5495                !!!parse-error (type => 'unmatched end tag:select', token => $token);
5496                ## Ignore the token
5497                !!!nack ('t280.1');
5498                !!!next-token;
5499                next B;
5500              }
5501                                
5502                !!!cp ('t281');            !!!cp ('t281');
5503                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5504    
5505                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5506    
5507                !!!next-token;            if ($token->{tag_name} eq 'select') {
5508                redo B;              !!!nack ('t281.2');
5509                !!!next-token;
5510                next B;
5511              } else {
5512                !!!cp ('t281.1');
5513                !!!ack-later;
5514                ## Reprocess the token.
5515                next B;
5516              }
5517          } else {          } else {
5518            !!!cp ('t282');            !!!cp ('t282');
5519            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);
5520            ## Ignore the token            ## Ignore the token
5521              !!!nack ('t282.1');
5522            !!!next-token;            !!!next-token;
5523            redo B;            next B;
5524          }          }
5525        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5526              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
5527                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
5528                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
5529                  !!!cp ('t283');              !!!cp ('t283');
5530                  ## As if </option>              ## As if </option>
5531                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
5532                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
5533                  !!!cp ('t284');              !!!cp ('t284');
5534                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
5535                } else {            } else {
5536                  !!!cp ('t285');              !!!cp ('t285');
5537                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5538                  ## Ignore the token              ## Ignore the token
5539                }            }
5540                !!!next-token;            !!!nack ('t285.1');
5541                redo B;            !!!next-token;
5542              } elsif ($token->{tag_name} eq 'option') {            next B;
5543                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
5544                  !!!cp ('t286');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
5545                  pop @{$self->{open_elements}};              !!!cp ('t286');
5546                } else {              pop @{$self->{open_elements}};
5547                  !!!cp ('t287');            } else {
5548                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t287');
5549                  ## Ignore the token              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5550                }              ## Ignore the token
5551                !!!next-token;            }
5552                redo B;            !!!nack ('t287.1');
5553              } elsif ($token->{tag_name} eq 'select') {            !!!next-token;
5554                ## have an element in table scope            next B;
5555                my $i;          } elsif ($token->{tag_name} eq 'select') {
5556                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            ## have an element in table scope
5557                  my $node = $self->{open_elements}->[$_];            my $i;
5558                  if ($node->[1] eq $token->{tag_name}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5559                    !!!cp ('t288');              my $node = $self->{open_elements}->[$_];
5560                    $i = $_;              if ($node->[1] & SELECT_EL) {
5561                    last INSCOPE;                !!!cp ('t288');
5562                  } elsif ({                $i = $_;
5563                            table => 1, html => 1,                last INSCOPE;
5564                           }->{$node->[1]}) {              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5565                    !!!cp ('t289');                !!!cp ('t289');
5566                    last INSCOPE;                last INSCOPE;
5567                  }              }
5568                } # INSCOPE            } # INSCOPE
5569                unless (defined $i) {            unless (defined $i) {
5570                  !!!cp ('t290');              !!!cp ('t290');
5571                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5572                  ## Ignore the token              ## Ignore the token
5573                  !!!next-token;              !!!nack ('t290.1');
5574                  redo B;              !!!next-token;
5575                }              next B;
5576              }
5577                                
5578                !!!cp ('t291');            !!!cp ('t291');
5579                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5580    
5581                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5582    
5583                !!!next-token;            !!!nack ('t291.1');
5584                redo B;            !!!next-token;
5585              } elsif ({            next B;
5586                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
5587                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
5588                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
5589                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
5590                     }->{$token->{tag_name}}) {
5591  ## TODO: The following is wrong?  ## TODO: The following is wrong?
5592                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5593                                
5594                ## have an element in table scope            ## have an element in table scope
5595                my $i;            my $i;
5596                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5597                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
5598                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5599                    !!!cp ('t292');                !!!cp ('t292');
5600                    $i = $_;                $i = $_;
5601                    last INSCOPE;                last INSCOPE;
5602                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
5603                            table => 1, html => 1,                !!!cp ('t293');
5604                           }->{$node->[1]}) {                last INSCOPE;
5605                    !!!cp ('t293');              }
5606                    last INSCOPE;            } # INSCOPE
5607                  }            unless (defined $i) {
5608                } # INSCOPE              !!!cp ('t294');
5609                unless (defined $i) {              ## Ignore the token
5610                  !!!cp ('t294');              !!!nack ('t294.1');
5611                  ## Ignore the token              !!!next-token;
5612                  !!!next-token;              next B;
5613                  redo B;            }
               }  
5614                                
5615                ## As if </select>            ## As if </select>
5616                ## have an element in table scope            ## have an element in table scope
5617                undef $i;            undef $i;
5618                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5619                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
5620                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
5621                    !!!cp ('t295');                !!!cp ('t295');
5622                    $i = $_;                $i = $_;
5623                    last INSCOPE;                last INSCOPE;
5624                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5625  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5626                    !!!cp ('t296');                !!!cp ('t296');
5627                    last INSCOPE;                last INSCOPE;
5628                  }              }
5629                } # INSCOPE            } # INSCOPE
5630                unless (defined $i) {            unless (defined $i) {
5631                  !!!cp ('t297');              !!!cp ('t297');
5632  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
5633                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag:select', token => $token);
5634                  ## Ignore the </select> token              ## Ignore the </select> token
5635                  !!!next-token; ## TODO: ok?              !!!nack ('t297.1');
5636                  redo B;              !!!next-token; ## TODO: ok?
5637                }              next B;
5638              }
5639                                
5640                !!!cp ('t298');            !!!cp ('t298');
5641                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5642    
5643                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
5644    
5645                ## reprocess            !!!ack-later;
5646                redo B;            ## reprocess
5647              next B;
5648          } else {          } else {
5649            !!!cp ('t299');            !!!cp ('t299');
5650            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);
5651            ## Ignore the token            ## Ignore the token
5652              !!!nack ('t299.3');
5653            !!!next-token;            !!!next-token;
5654            redo B;            next B;
5655            }
5656          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5657            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5658                    @{$self->{open_elements}} == 1) { # redundant, maybe
5659              !!!cp ('t299.1');
5660              !!!parse-error (type => 'in body:#eof', token => $token);
5661            } else {
5662              !!!cp ('t299.2');
5663          }          }
5664    
5665            ## Stop parsing.
5666            last B;
5667        } else {        } else {
5668          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5669        }        }
# Line 5086  sub _tree_construction_main ($) { Line 5679  sub _tree_construction_main ($) {
5679            unless (length $token->{data}) {            unless (length $token->{data}) {
5680              !!!cp ('t300');              !!!cp ('t300');
5681              !!!next-token;              !!!next-token;
5682              redo B;              next B;
5683            }            }
5684          }          }
5685                    
5686          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5687            !!!cp ('t301');            !!!cp ('t301');
5688            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#character', token => $token);
5689    
5690            ## Reprocess in the "after body" insertion mode.            ## Reprocess in the "after body" insertion mode.
5691          } else {          } else {
# Line 5100  sub _tree_construction_main ($) { Line 5693  sub _tree_construction_main ($) {
5693          }          }
5694                    
5695          ## "after body" insertion mode          ## "after body" insertion mode
5696          !!!parse-error (type => 'after body:#character');          !!!parse-error (type => 'after body:#character', token => $token);
5697    
5698          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5699          ## reprocess          ## reprocess
5700          redo B;          next B;
5701        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5702          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5703            !!!cp ('t303');            !!!cp ('t303');
5704            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5705                        
5706            ## Reprocess in the "after body" insertion mode.            ## Reprocess in the "after body" insertion mode.
5707          } else {          } else {
# Line 5116  sub _tree_construction_main ($) { Line 5709  sub _tree_construction_main ($) {
5709          }          }
5710    
5711          ## "after body" insertion mode          ## "after body" insertion mode
5712          !!!parse-error (type => 'after body:'.$token->{tag_name});          !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);
5713    
5714          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5715            !!!ack-later;
5716          ## reprocess          ## reprocess
5717          redo B;          next B;
5718        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5719          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
5720            !!!cp ('t305');            !!!cp ('t305');
5721            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5722                        
5723            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
5724            ## Reprocess in the "after body" insertion mode.            ## Reprocess in the "after body" insertion mode.
# Line 5136  sub _tree_construction_main ($) { Line 5730  sub _tree_construction_main ($) {
5730          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
5731            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
5732              !!!cp ('t307');              !!!cp ('t307');
5733              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag:html', token => $token);
5734              ## Ignore the token              ## Ignore the token
5735              !!!next-token;              !!!next-token;
5736              redo B;              next B;
5737            } else {            } else {
5738              !!!cp ('t308');              !!!cp ('t308');
5739              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
5740              !!!next-token;              !!!next-token;
5741              redo B;              next B;
5742            }            }
5743          } else {          } else {
5744            !!!cp ('t309');            !!!cp ('t309');
5745            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);
5746    
5747            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
5748            ## reprocess            ## reprocess
5749            redo B;            next B;
5750          }          }
5751          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5752            !!!cp ('t309.2');
5753            ## Stop parsing
5754            last B;
5755        } else {        } else {
5756          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5757        }        }
# Line 5165  sub _tree_construction_main ($) { Line 5763  sub _tree_construction_main ($) {
5763            unless (length $token->{data}) {            unless (length $token->{data}) {
5764              !!!cp ('t310');              !!!cp ('t310');
5765              !!!next-token;              !!!next-token;
5766              redo B;              next B;
5767            }            }
5768          }          }
5769                    
5770          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
5771            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5772              !!!cp ('t311');              !!!cp ('t311');
5773              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#character', token => $token);
5774            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
5775              !!!cp ('t312');              !!!cp ('t312');
5776              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#character', token => $token);
5777            } else { # "after html frameset"            } else { # "after html frameset"
5778              !!!cp ('t313');              !!!cp ('t313');
5779              !!!parse-error (type => 'after html:#character');              !!!parse-error (type => 'after html:#character', token => $token);
5780    
5781              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5782              ## Reprocess in the "after frameset" insertion mode.              ## Reprocess in the "after frameset" insertion mode.
5783              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#character', token => $token);
5784            }            }
5785                        
5786            ## Ignore the token.            ## Ignore the token.
# Line 5193  sub _tree_construction_main ($) { Line 5791  sub _tree_construction_main ($) {
5791              !!!cp ('t315');              !!!cp ('t315');
5792              !!!next-token;              !!!next-token;
5793            }            }
5794            redo B;            next B;
5795          }          }
5796                    
5797          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
5798        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5799          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5800            !!!cp ('t316');            !!!cp ('t316');
5801            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);
5802    
5803            $self->{insertion_mode} = AFTER_FRAMESET_IM;            $self->{insertion_mode} = AFTER_FRAMESET_IM;
5804            ## Process in the "after frameset" insertion mode.            ## Process in the "after frameset" insertion mode.
# Line 5211  sub _tree_construction_main ($) { Line 5809  sub _tree_construction_main ($) {
5809          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
5810              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
5811            !!!cp ('t318');            !!!cp ('t318');
5812            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5813              !!!nack ('t318.1');
5814            !!!next-token;            !!!next-token;
5815            redo B;            next B;
5816          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
5817                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
5818            !!!cp ('t319');            !!!cp ('t319');
5819            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5820            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
5821              !!!ack ('t319.1');
5822            !!!next-token;            !!!next-token;
5823            redo B;            next B;
5824          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
5825            !!!cp ('t320');            !!!cp ('t320');
5826            ## NOTE: As if in body.            ## NOTE: As if in body.
5827            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            $parse_rcdata->(CDATA_CONTENT_MODEL);
5828            redo B;            next B;
5829          } else {          } else {
5830            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5831              !!!cp ('t321');              !!!cp ('t321');
5832              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);
5833            } else {            } else {
5834              !!!cp ('t322');              !!!cp ('t322');
5835              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);
5836            }            }
5837            ## Ignore the token            ## Ignore the token
5838              !!!nack ('t322.1');
5839            !!!next-token;            !!!next-token;
5840            redo B;            next B;
5841          }          }
5842        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5843          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
5844            !!!cp ('t323');            !!!cp ('t323');
5845            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);
5846    
5847            $self->{insertion_mode} = AFTER_FRAMESET_IM;            $self->{insertion_mode} = AFTER_FRAMESET_IM;
5848            ## Process in the "after frameset" insertion mode.            ## Process in the "after frameset" insertion mode.
# Line 5251  sub _tree_construction_main ($) { Line 5852  sub _tree_construction_main ($) {
5852    
5853          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
5854              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
5855            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5856                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
5857              !!!cp ('t325');              !!!cp ('t325');
5858              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
5859              ## Ignore the token              ## Ignore the token
5860              !!!next-token;              !!!next-token;
5861            } else {            } else {
# Line 5264  sub _tree_construction_main ($) { Line 5865  sub _tree_construction_main ($) {
5865            }            }
5866    
5867            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
5868                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
5869              !!!cp ('t327');              !!!cp ('t327');
5870              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
5871            } else {            } else {
5872              !!!cp ('t328');              !!!cp ('t328');
5873            }            }
5874            redo B;            next B;
5875          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
5876                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
5877            !!!cp ('t329');            !!!cp ('t329');
5878            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
5879            !!!next-token;            !!!next-token;
5880            redo B;            next B;
5881          } else {          } else {
5882            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
5883              !!!cp ('t330');              !!!cp ('t330');
5884              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);
5885            } else {            } else {
5886              !!!cp ('t331');              !!!cp ('t331');
5887              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);
5888            }            }
5889            ## Ignore the token            ## Ignore the token
5890            !!!next-token;            !!!next-token;
5891            redo B;            next B;
5892            }
5893          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5894            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5895                    @{$self->{open_elements}} == 1) { # redundant, maybe
5896              !!!cp ('t331.1');
5897              !!!parse-error (type => 'in body:#eof', token => $token);
5898            } else {
5899              !!!cp ('t331.2');
5900          }          }
5901            
5902            ## Stop parsing
5903            last B;
5904        } else {        } else {
5905          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5906        }        }
# Line 5303  sub _tree_construction_main ($) { Line 5915  sub _tree_construction_main ($) {
5915        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
5916          !!!cp ('t332');          !!!cp ('t332');
5917          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5918          $script_start_tag->($insert);          $script_start_tag->();
5919          redo B;          next B;
5920        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
5921          !!!cp ('t333');          !!!cp ('t333');
5922          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5923          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
5924          redo B;          next B;
5925        } elsif ({        } elsif ({
5926                  base => 1, link => 1,                  base => 1, link => 1,
5927                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5928          !!!cp ('t334');          !!!cp ('t334');
5929          ## 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
5930          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5931          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
5932            !!!ack ('t334.1');
5933          !!!next-token;          !!!next-token;
5934          redo B;          next B;
5935        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
5936          ## 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
5937          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5938          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.
5939    
5940          unless ($self->{confident}) {          unless ($self->{confident}) {
5941            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) { ## TODO: And if supported
5942              !!!cp ('t335');              !!!cp ('t335');
5943              $self->{change_encoding}              $self->{change_encoding}
5944                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
5945                            
5946              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
5947                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
# Line 5343  sub _tree_construction_main ($) { Line 5956  sub _tree_construction_main ($) {
5956                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
5957                !!!cp ('t336');                !!!cp ('t336');
5958                $self->{change_encoding}                $self->{change_encoding}
5959                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
5960                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
5961                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
5962                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5367  sub _tree_construction_main ($) { Line 5980  sub _tree_construction_main ($) {
5980            }            }
5981          }          }
5982    
5983            !!!ack ('t338.1');
5984          !!!next-token;          !!!next-token;
5985          redo B;          next B;
5986        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
5987          !!!cp ('t341');          !!!cp ('t341');
         !!!parse-error (type => 'in body:title');  
5988          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
5989          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
5990            if (defined $self->{head_element}) {          next B;
             !!!cp ('t339');  
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             !!!cp ('t340');  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
5991        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
5992          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body:body', token => $token);
5993                                
5994          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
5995              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
5996            !!!cp ('t342');            !!!cp ('t342');
5997            ## Ignore the token            ## Ignore the token
5998          } else {          } else {
# Line 5401  sub _tree_construction_main ($) { Line 6006  sub _tree_construction_main ($) {
6006              }              }
6007            }            }
6008          }          }
6009            !!!nack ('t343.1');
6010          !!!next-token;          !!!next-token;
6011          redo B;          next B;
6012        } elsif ({        } elsif ({
6013                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
6014                  div => 1, dl => 1, fieldset => 1,                  div => 1, dl => 1, fieldset => 1,
6015                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6016                  listing => 1, menu => 1, ol => 1, p => 1, ul => 1,                  menu => 1, ol => 1, p => 1, ul => 1,
6017                  pre => 1,                  pre => 1, listing => 1,
6018                    form => 1,
6019                    table => 1,
6020                    hr => 1,
6021                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6022            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6023              !!!cp ('t350');
6024              !!!parse-error (type => 'in form:form', token => $token);
6025              ## Ignore the token
6026              !!!nack ('t350.1');
6027              !!!next-token;
6028              next B;
6029            }
6030    
6031          ## has a p element in scope          ## has a p element in scope
6032          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6033            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6034              !!!cp ('t344');              !!!cp ('t344');
6035              !!!back-token;              !!!back-token; # <form>
6036              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6037              redo B;                        line => $token->{line}, column => $token->{column}};
6038            } elsif ({              next B;
6039                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6040              !!!cp ('t345');              !!!cp ('t345');
6041              last INSCOPE;              last INSCOPE;
6042            }            }
6043          } # INSCOPE          } # INSCOPE
6044                        
6045          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6046          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6047              !!!nack ('t346.1');
6048            !!!next-token;            !!!next-token;
6049            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6050              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5440  sub _tree_construction_main ($) { Line 6057  sub _tree_construction_main ($) {
6057            } else {            } else {
6058              !!!cp ('t348');              !!!cp ('t348');
6059            }            }
6060          } else {          } elsif ($token->{tag_name} eq 'form') {
6061            !!!cp ('t347');            !!!cp ('t347.1');
6062              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6063    
6064              !!!nack ('t347.2');
6065            !!!next-token;            !!!next-token;
6066          }          } elsif ($token->{tag_name} eq 'table') {
6067          redo B;            !!!cp ('t382');
6068        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6069          if (defined $self->{form_element}) {            
6070            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
6071            !!!parse-error (type => 'in form:form');  
6072            ## Ignore the token            !!!nack ('t382.1');
6073              !!!next-token;
6074            } elsif ($token->{tag_name} eq 'hr') {
6075              !!!cp ('t386');
6076              pop @{$self->{open_elements}};
6077            
6078              !!!nack ('t386.1');
6079            !!!next-token;            !!!next-token;
           redo B;  
6080          } else {          } else {
6081            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!cp ('t351');  
               !!!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]}) {  
               !!!cp ('t352');  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
6082            !!!next-token;            !!!next-token;
           redo B;  
6083          }          }
6084        } elsif ($token->{tag_name} eq 'li') {          next B;
6085          } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6086          ## has a p element in scope          ## has a p element in scope
6087          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6088            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6089              !!!cp ('t353');              !!!cp ('t353');
6090              !!!back-token;              !!!back-token; # <x>
6091              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6092              redo B;                        line => $token->{line}, column => $token->{column}};
6093            } elsif ({              next B;
6094                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6095              !!!cp ('t354');              !!!cp ('t354');
6096              last INSCOPE;              last INSCOPE;
6097            }            }
# Line 5494  sub _tree_construction_main ($) { Line 6100  sub _tree_construction_main ($) {
6100          ## Step 1          ## Step 1
6101          my $i = -1;          my $i = -1;
6102          my $node = $self->{open_elements}->[$i];          my $node = $self->{open_elements}->[$i];
6103            my $li_or_dtdd = {li => {li => 1},
6104                              dt => {dt => 1, dd => 1},
6105                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6106          LI: {          LI: {
6107            ## Step 2            ## Step 2
6108            if ($node->[1] eq 'li') {            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6109              if ($i != -1) {              if ($i != -1) {
6110                !!!cp ('t355');                !!!cp ('t355');
6111                !!!parse-error (type => 'end tag missing:'.                !!!parse-error (type => 'not closed',
6112                                $self->{open_elements}->[-1]->[1]);                                value => $self->{open_elements}->[-1]->[0]
6113                                      ->manakai_local_name,
6114                                  token => $token);
6115              } else {              } else {
6116                !!!cp ('t356');                !!!cp ('t356');
6117              }              }
# Line 5511  sub _tree_construction_main ($) { Line 6122  sub _tree_construction_main ($) {
6122            }            }
6123                        
6124            ## Step 3            ## Step 3
6125            if (not $formatting_category->{$node->[1]} and            if (not ($node->[1] & FORMATTING_EL) and
6126                #not $phrasing_category->{$node->[1]} and                #not $phrasing_category->{$node->[1]} and
6127                ($special_category->{$node->[1]} or                ($node->[1] & SPECIAL_EL or
6128                 $scoping_category->{$node->[1]}) and                 $node->[1] & SCOPING_EL) and
6129                $node->[1] ne 'address' and $node->[1] ne 'div') {                not ($node->[1] & ADDRESS_EL) and
6130                  not ($node->[1] & DIV_EL)) {
6131              !!!cp ('t358');              !!!cp ('t358');
6132              last LI;              last LI;
6133            }            }
# Line 5527  sub _tree_construction_main ($) { Line 6139  sub _tree_construction_main ($) {
6139            redo LI;            redo LI;
6140          } # LI          } # LI
6141                        
6142          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6143            !!!nack ('t359.1');
6144          !!!next-token;          !!!next-token;
6145          redo B;          next B;
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!cp ('t360');  
             !!!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]}) {  
             !!!cp ('t361');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!cp ('t362');  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             } else {  
               !!!cp ('t363');  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           } else {  
             !!!cp ('t364');  
           }  
             
           ## 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') {  
             !!!cp ('t365');  
             last LI;  
           }  
             
           !!!cp ('t366');  
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
6146        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
6147          ## has a p element in scope          ## has a p element in scope
6148          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6149            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6150              !!!cp ('t367');              !!!cp ('t367');
6151              !!!back-token;              !!!back-token; # <plaintext>
6152              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6153              redo B;                        line => $token->{line}, column => $token->{column}};
6154            } elsif ({              next B;
6155                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6156              !!!cp ('t368');              !!!cp ('t368');
6157              last INSCOPE;              last INSCOPE;
6158            }            }
6159          } # INSCOPE          } # INSCOPE
6160                        
6161          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6162                        
6163          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
6164                        
6165            !!!nack ('t368.1');
6166          !!!next-token;          !!!next-token;
6167          redo B;          next B;
6168        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
6169          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
6170            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
6171            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
6172              !!!cp ('t371');              !!!cp ('t371');
6173              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
6174                            
6175              !!!back-token;              !!!back-token; # <a>
6176              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
6177              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
6178                $formatting_end_tag->($token);
6179                            
6180              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
6181                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5643  sub _tree_construction_main ($) { Line 6200  sub _tree_construction_main ($) {
6200                        
6201          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6202    
6203          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6204          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
6205    
6206            !!!nack ('t374.1');
6207          !!!next-token;          !!!next-token;
6208          redo B;          next 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}}) {  
         !!!cp ('t375');  
         $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;  
6209        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
6210          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6211    
6212          ## has a |nobr| element in scope          ## has a |nobr| element in scope
6213          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6214            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6215            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
6216              !!!cp ('t376');              !!!cp ('t376');
6217              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
6218              !!!back-token;              !!!back-token; # <nobr>
6219              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
6220              redo B;                        line => $token->{line}, column => $token->{column}};
6221            } elsif ({              next B;
6222                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6223              !!!cp ('t377');              !!!cp ('t377');
6224              last INSCOPE;              last INSCOPE;
6225            }            }
6226          } # INSCOPE          } # INSCOPE
6227                    
6228          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6229          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
6230                    
6231            !!!nack ('t377.1');
6232          !!!next-token;          !!!next-token;
6233          redo B;          next B;
6234        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
6235          ## has a button element in scope          ## has a button element in scope
6236          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6237            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6238            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
6239              !!!cp ('t378');              !!!cp ('t378');
6240              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
6241              !!!back-token;              !!!back-token; # <button>
6242              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
6243              redo B;                        line => $token->{line}, column => $token->{column}};
6244            } elsif ({              next B;
6245                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6246              !!!cp ('t379');              !!!cp ('t379');
6247              last INSCOPE;              last INSCOPE;
6248            }            }
# Line 5708  sub _tree_construction_main ($) { Line 6250  sub _tree_construction_main ($) {
6250                        
6251          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6252                        
6253          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6254    
6255          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
6256    
6257          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
6258    
6259            !!!nack ('t379.1');
6260          !!!next-token;          !!!next-token;
6261          redo B;          next B;
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         !!!cp ('t380');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         !!!cp ('t381');  
         $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') {  
             !!!cp ('t382');  
             !!!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]}) {  
             !!!cp ('t383');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo B;  
6262        } elsif ({        } elsif ({
6263                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
6264                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
6265                  image => 1,                  noembed => 1,
6266                    noframes => 1,
6267                    noscript => 0, ## TODO: 1 if scripting is enabled
6268                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6269          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
6270            !!!cp ('t384');            !!!cp ('t381');
6271            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
6272          } else {          } else {
6273            !!!cp ('t385');            !!!cp ('t399');
6274          }          }
6275            ## NOTE: There is an "as if in body" code clone.
6276          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
6277          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!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') {  
             !!!cp ('t386');  
             !!!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]}) {  
             !!!cp ('t387');  
             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') {  
         !!!cp ('t388');  
         $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;  
         redo B;  
6278        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
6279          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
6280                    
6281          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
6282            !!!cp ('t389');            !!!cp ('t389');
6283            ## Ignore the token            ## Ignore the token
6284              !!!nack ('t389'); ## NOTE: Not acknowledged.
6285            !!!next-token;            !!!next-token;
6286            redo B;            next B;
6287          } else {          } else {
6288            my $at = $token->{attributes};            my $at = $token->{attributes};
6289            my $form_attrs;            my $form_attrs;
# Line 5825  sub _tree_construction_main ($) { Line 6294  sub _tree_construction_main ($) {
6294            delete $at->{prompt};            delete $at->{prompt};
6295            my @tokens = (            my @tokens = (
6296                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
6297                           attributes => $form_attrs},                           attributes => $form_attrs,
6298                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
6299                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
6300                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
6301                            {type => START_TAG_TOKEN, tag_name => 'p',
6302                             line => $token->{line}, column => $token->{column}},
6303                            {type => START_TAG_TOKEN, tag_name => 'label',
6304                             line => $token->{line}, column => $token->{column}},
6305                         );                         );
6306            if ($prompt_attr) {            if ($prompt_attr) {
6307              !!!cp ('t390');              !!!cp ('t390');
6308              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
6309                               #line => $token->{line}, column => $token->{column},
6310                              };
6311            } else {            } else {
6312              !!!cp ('t391');              !!!cp ('t391');
6313              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
6314                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
6315                               #line => $token->{line}, column => $token->{column},
6316                              }; # SHOULD
6317              ## TODO: make this configurable              ## TODO: make this configurable
6318            }            }
6319            push @tokens,            push @tokens,
6320                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
6321                             line => $token->{line}, column => $token->{column}},
6322                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
6323                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
6324                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
6325                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
6326                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
6327            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
6328                             line => $token->{line}, column => $token->{column}},
6329                            {type => END_TAG_TOKEN, tag_name => 'form',
6330                             line => $token->{line}, column => $token->{column}};
6331              !!!nack ('t391.1'); ## NOTE: Not acknowledged.
6332            !!!back-token (@tokens);            !!!back-token (@tokens);
6333            redo B;            !!!next-token;
6334              next B;
6335          }          }
6336        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
6337          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
6338          my $el;          my $el;
6339          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
6340                    
6341          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
6342          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5862  sub _tree_construction_main ($) { Line 6345  sub _tree_construction_main ($) {
6345          $insert->($el);          $insert->($el);
6346                    
6347          my $text = '';          my $text = '';
6348            !!!nack ('t392.1');
6349          !!!next-token;          !!!next-token;
6350          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
6351            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 5892  sub _tree_construction_main ($) { Line 6376  sub _tree_construction_main ($) {
6376            ## Ignore the token            ## Ignore the token
6377          } else {          } else {
6378            !!!cp ('t398');            !!!cp ('t398');
6379            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);
6380          }          }
6381          !!!next-token;          !!!next-token;
6382          redo B;          next B;
6383        } elsif ({        } elsif ($token->{tag_name} eq 'math' or
6384                  iframe => 1,                 $token->{tag_name} eq 'svg') {
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t399');  
         ## NOTE: There is an "as if in body" code clone.  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'select') {  
         !!!cp ('t400');  
6385          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6386                    
6387          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token);
   
         ## TODO: associate with $self->{form_element} if defined  
6388                    
6389          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
6390              pop @{$self->{open_elements}};
6391              !!!ack ('t398.1');
6392            } else {
6393              !!!cp ('t398.2');
6394              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
6395              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
6396              ## mode, "in body" (not "in foreign content") secondary insertion
6397              ## mode, maybe.
6398            }
6399    
6400          !!!next-token;          !!!next-token;
6401          redo B;          next B;
6402        } elsif ({        } elsif ({
6403                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
6404                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 5924  sub _tree_construction_main ($) { Line 6406  sub _tree_construction_main ($) {
6406                  thead => 1, tr => 1,                  thead => 1, tr => 1,
6407                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6408          !!!cp ('t401');          !!!cp ('t401');
6409          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);
6410          ## Ignore the token          ## Ignore the token
6411            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
6412          !!!next-token;          !!!next-token;
6413          redo B;          next B;
6414                    
6415          ## ISSUE: An issue on HTML5 new elements in the spec.          ## ISSUE: An issue on HTML5 new elements in the spec.
6416        } else {        } else {
6417          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
6418              !!!cp ('t384');
6419              !!!parse-error (type => 'image', token => $token);
6420              $token->{tag_name} = 'img';
6421            } else {
6422              !!!cp ('t385');
6423            }
6424    
6425            ## NOTE: There is an "as if <br>" code clone.
6426          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6427                    
6428          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6429    
6430            if ({
6431                 applet => 1, marquee => 1, object => 1,
6432                }->{$token->{tag_name}}) {
6433              !!!cp ('t380');
6434              push @$active_formatting_elements, ['#marker', ''];
6435              !!!nack ('t380.1');
6436            } elsif ({
6437                      b => 1, big => 1, em => 1, font => 1, i => 1,
6438                      s => 1, small => 1, strile => 1,
6439                      strong => 1, tt => 1, u => 1,
6440                     }->{$token->{tag_name}}) {
6441              !!!cp ('t375');
6442              push @$active_formatting_elements, $self->{open_elements}->[-1];
6443              !!!nack ('t375.1');
6444            } elsif ($token->{tag_name} eq 'input') {
6445              !!!cp ('t388');
6446              ## TODO: associate with $self->{form_element} if defined
6447              pop @{$self->{open_elements}};
6448              !!!ack ('t388.2');
6449            } elsif ({
6450                      area => 1, basefont => 1, bgsound => 1, br => 1,
6451                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
6452                      #image => 1,
6453                     }->{$token->{tag_name}}) {
6454              !!!cp ('t388.1');
6455              pop @{$self->{open_elements}};
6456              !!!ack ('t388.3');
6457            } elsif ($token->{tag_name} eq 'select') {
6458              ## TODO: associate with $self->{form_element} if defined
6459            
6460              if ($self->{insertion_mode} & TABLE_IMS or
6461                  $self->{insertion_mode} & BODY_TABLE_IMS or
6462                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6463                !!!cp ('t400.1');
6464                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
6465              } else {
6466                !!!cp ('t400.2');
6467                $self->{insertion_mode} = IN_SELECT_IM;
6468              }
6469              !!!nack ('t400.3');
6470            } else {
6471              !!!nack ('t402');
6472            }
6473                    
6474          !!!next-token;          !!!next-token;
6475          redo B;          next B;
6476        }        }
6477      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
6478        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
6479          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
6480              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
6481            for (@{$self->{open_elements}}) {          INSCOPE: {
6482              unless ({            for (reverse @{$self->{open_elements}}) {
6483                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
6484                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
6485                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
6486                      }->{$_->[1]}) {                last INSCOPE;
6487                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
6488                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
6489              } else {                last;
               !!!cp ('t404');  
6490              }              }
6491            }            }
6492    
6493            $self->{insertion_mode} = AFTER_BODY_IM;            !!!parse-error (type => 'start tag not allowed',
6494                              value => $token->{tag_name}, token => $token);
6495              ## NOTE: Ignore the token.
6496            !!!next-token;            !!!next-token;
6497            redo B;            next B;
6498          } else {          } # INSCOPE
6499            !!!cp ('t405');  
6500            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          for (@{$self->{open_elements}}) {
6501            ## Ignore the token            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
6502            !!!next-token;              !!!cp ('t403');
6503            redo B;              !!!parse-error (type => 'not closed',
6504                                value => $_->[0]->manakai_local_name,
6505                                token => $token);
6506                last;
6507              } else {
6508                !!!cp ('t404');
6509              }
6510          }          }
6511    
6512            $self->{insertion_mode} = AFTER_BODY_IM;
6513            !!!next-token;
6514            next B;
6515        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
6516          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
6517            ## up-to-date, though it has same effect as speced.
6518            if (@{$self->{open_elements}} > 1 and
6519                $self->{open_elements}->[1]->[1] & BODY_EL) {
6520            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
6521            if ($self->{open_elements}->[-1]->[1] ne 'body') {            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
6522              !!!cp ('t406');              !!!cp ('t406');
6523              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
6524                                value => $self->{open_elements}->[1]->[0]
6525                                    ->manakai_local_name,
6526                                token => $token);
6527            } else {            } else {
6528              !!!cp ('t407');              !!!cp ('t407');
6529            }            }
6530            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
6531            ## reprocess            ## reprocess
6532            redo B;            next B;
6533          } else {          } else {
6534            !!!cp ('t408');            !!!cp ('t408');
6535            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6536            ## Ignore the token            ## Ignore the token
6537            !!!next-token;            !!!next-token;
6538            redo B;            next B;
6539          }          }
6540        } elsif ({        } elsif ({
6541                  address => 1, blockquote => 1, center => 1, dir => 1,                  address => 1, blockquote => 1, center => 1, dir => 1,
6542                  div => 1, dl => 1, fieldset => 1, listing => 1,                  div => 1, dl => 1, fieldset => 1, listing => 1,
6543                  menu => 1, ol => 1, pre => 1, ul => 1,                  menu => 1, ol => 1, pre => 1, ul => 1,
                 p => 1,  
6544                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
6545                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
6546                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6547          ## has an element in scope          ## has an element in scope
6548          my $i;          my $i;
6549          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6550            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6551            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
             ## generate implied end tags  
             while ({  
                     dd => ($token->{tag_name} ne 'dd'),  
                     dt => ($token->{tag_name} ne 'dt'),  
                     li => ($token->{tag_name} ne 'li'),  
                     p => ($token->{tag_name} ne 'p'),  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t409');  
               pop @{$self->{open_elements}};  
             }  
               
6552              !!!cp ('t410');              !!!cp ('t410');
6553              $i = $_;              $i = $_;
6554              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
6555            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6556              !!!cp ('t411');              !!!cp ('t411');
6557              last INSCOPE;              last INSCOPE;
6558            }            }
6559          } # INSCOPE          } # INSCOPE
6560            
6561          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
6562            if (defined $i) {            !!!cp ('t413');
6563              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6564            } else {
6565              ## Step 1. generate implied end tags
6566              while ({
6567                      dd => ($token->{tag_name} ne 'dd'),
6568                      dt => ($token->{tag_name} ne 'dt'),
6569                      li => ($token->{tag_name} ne 'li'),
6570                      p => 1,
6571                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
6572                !!!cp ('t409');
6573                pop @{$self->{open_elements}};
6574              }
6575    
6576              ## Step 2.
6577              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6578                      ne $token->{tag_name}) {
6579              !!!cp ('t412');              !!!cp ('t412');
6580              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
6581                                value => $self->{open_elements}->[-1]->[0]
6582                                    ->manakai_local_name,
6583                                token => $token);
6584            } else {            } else {
6585              !!!cp ('t413');              !!!cp ('t414');
             !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6586            }            }
6587          }  
6588                      ## Step 3.
         if (defined $i) {  
           !!!cp ('t414');  
6589            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6590          } elsif ($token->{tag_name} eq 'p') {  
6591            !!!cp ('t415');            ## Step 4.
6592            ## As if <p>, then reprocess the current token            $clear_up_to_marker->()
6593            my $el;                if {
6594            !!!create-element ($el, 'p');                  applet => 1, button => 1, marquee => 1, object => 1,
6595            $insert->($el);                }->{$token->{tag_name}};
         } else {  
           !!!cp ('t416');  
6596          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
6597          !!!next-token;          !!!next-token;
6598          redo B;          next B;
6599        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
6600            undef $self->{form_element};
6601    
6602          ## has an element in scope          ## has an element in scope
6603            my $i;
6604          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6605            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6606            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
             ## generate implied end tags  
             while ({  
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t417');  
               pop @{$self->{open_elements}};  
             }  
   
6607              !!!cp ('t418');              !!!cp ('t418');
6608                $i = $_;
6609              last INSCOPE;              last INSCOPE;
6610            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6611              !!!cp ('t419');              !!!cp ('t419');
6612              last INSCOPE;              last INSCOPE;
6613            }            }
6614          } # INSCOPE          } # INSCOPE
6615            
6616          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
           !!!cp ('t420');  
           pop @{$self->{open_elements}};  
         } else {  
6617            !!!cp ('t421');            !!!cp ('t421');
6618            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6619            } else {
6620              ## Step 1. generate implied end tags
6621              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6622                !!!cp ('t417');
6623                pop @{$self->{open_elements}};
6624              }
6625              
6626              ## Step 2.
6627              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6628                      ne $token->{tag_name}) {
6629                !!!cp ('t417.1');
6630                !!!parse-error (type => 'not closed',
6631                                value => $self->{open_elements}->[-1]->[0]
6632                                    ->manakai_local_name,
6633                                token => $token);
6634              } else {
6635                !!!cp ('t420');
6636              }  
6637              
6638              ## Step 3.
6639              splice @{$self->{open_elements}}, $i;
6640          }          }
6641    
         undef $self->{form_element};  
6642          !!!next-token;          !!!next-token;
6643          redo B;          next B;
6644        } elsif ({        } elsif ({
6645                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6646                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 6091  sub _tree_construction_main ($) { Line 6648  sub _tree_construction_main ($) {
6648          my $i;          my $i;
6649          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6650            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
6651            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             while ({  
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!cp ('t422');  
               pop @{$self->{open_elements}};  
             }  
   
6652              !!!cp ('t423');              !!!cp ('t423');
6653              $i = $_;              $i = $_;
6654              last INSCOPE;              last INSCOPE;
6655            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
6656              !!!cp ('t424');              !!!cp ('t424');
6657              last INSCOPE;              last INSCOPE;
6658            }            }
6659          } # INSCOPE          } # INSCOPE
6660    
6661            unless (defined $i) { # has an element in scope
6662              !!!cp ('t425.1');
6663              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6664            } else {
6665              ## Step 1. generate implied end tags
6666              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
6667                !!!cp ('t422');
6668                pop @{$self->{open_elements}};
6669              }
6670              
6671              ## Step 2.
6672              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6673                      ne $token->{tag_name}) {
6674                !!!cp ('t425');
6675                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6676              } else {
6677                !!!cp ('t426');
6678              }
6679    
6680              ## Step 3.
6681              splice @{$self->{open_elements}}, $i;
6682            }
6683                    
6684          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          !!!next-token;
6685            !!!cp ('t425');          next B;
6686            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});        } elsif ($token->{tag_name} eq 'p') {
6687            ## has an element in scope
6688            my $i;
6689            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6690              my $node = $self->{open_elements}->[$_];
6691              if ($node->[1] & P_EL) {
6692                !!!cp ('t410.1');
6693                $i = $_;
6694                last INSCOPE;
6695              } elsif ($node->[1] & SCOPING_EL) {
6696                !!!cp ('t411.1');
6697                last INSCOPE;
6698              }
6699            } # INSCOPE
6700    
6701            if (defined $i) {
6702              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6703                      ne $token->{tag_name}) {
6704                !!!cp ('t412.1');
6705                !!!parse-error (type => 'not closed',
6706                                value => $self->{open_elements}->[-1]->[0]
6707                                    ->manakai_local_name,
6708                                token => $token);
6709              } else {
6710                !!!cp ('t414.1');
6711              }
6712    
6713              splice @{$self->{open_elements}}, $i;
6714          } else {          } else {
6715            !!!cp ('t426');            !!!cp ('t413.1');
6716              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6717    
6718              !!!cp ('t415.1');
6719              ## As if <p>, then reprocess the current token
6720              my $el;
6721              !!!create-element ($el, $HTML_NS, 'p',, $token);
6722              $insert->($el);
6723              ## NOTE: Not inserted into |$self->{open_elements}|.
6724          }          }
6725            
         splice @{$self->{open_elements}}, $i if defined $i;  
6726          !!!next-token;          !!!next-token;
6727          redo B;          next B;
6728        } elsif ({        } elsif ({
6729                  a => 1,                  a => 1,
6730                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
# Line 6131  sub _tree_construction_main ($) { Line 6732  sub _tree_construction_main ($) {
6732                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
6733                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6734          !!!cp ('t427');          !!!cp ('t427');
6735          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
6736          redo B;          next B;
6737        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
6738          !!!cp ('t428');          !!!cp ('t428');
6739          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag:br', token => $token);
6740    
6741          ## As if <br>          ## As if <br>
6742          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
6743                    
6744          my $el;          my $el;
6745          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
6746          $insert->($el);          $insert->($el);
6747                    
6748          ## Ignore the token.          ## Ignore the token.
6749          !!!next-token;          !!!next-token;
6750          redo B;          next B;
6751        } elsif ({        } elsif ({
6752                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
6753                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6160  sub _tree_construction_main ($) { Line 6761  sub _tree_construction_main ($) {
6761                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
6762                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6763          !!!cp ('t429');          !!!cp ('t429');
6764          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6765          ## Ignore the token          ## Ignore the token
6766          !!!next-token;          !!!next-token;
6767          redo B;          next B;
6768                    
6769          ## ISSUE: Issue on HTML5 new elements in spec          ## ISSUE: Issue on HTML5 new elements in spec
6770                    
# Line 6174  sub _tree_construction_main ($) { Line 6775  sub _tree_construction_main ($) {
6775    
6776          ## Step 2          ## Step 2
6777          S2: {          S2: {
6778            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6779              ## Step 1              ## Step 1
6780              ## generate implied end tags              ## generate implied end tags
6781              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
6782                !!!cp ('t430');                !!!cp ('t430');
6783                ## ISSUE: Can this case be reached?                ## ISSUE: Can this case be reached?
6784                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6785              }              }
6786                    
6787              ## Step 2              ## Step 2
6788              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
6789                        ne $token->{tag_name}) {
6790                !!!cp ('t431');                !!!cp ('t431');
6791                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
6792                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
6793                                  value => $self->{open_elements}->[-1]->[0]
6794                                      ->manakai_local_name,
6795                                  token => $token);
6796              } else {              } else {
6797                !!!cp ('t432');                !!!cp ('t432');
6798              }              }
# Line 6201  sub _tree_construction_main ($) { Line 6804  sub _tree_construction_main ($) {
6804              last S2;              last S2;
6805            } else {            } else {
6806              ## Step 3              ## Step 3
6807              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
6808                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
6809                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
6810                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
6811                !!!cp ('t433');                !!!cp ('t433');
6812                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);
6813                ## Ignore the token                ## Ignore the token
6814                !!!next-token;                !!!next-token;
6815                last S2;                last S2;
# Line 6222  sub _tree_construction_main ($) { Line 6825  sub _tree_construction_main ($) {
6825            ## Step 5;            ## Step 5;
6826            redo S2;            redo S2;
6827          } # S2          } # S2
6828          redo B;          next B;
6829        }        }
6830      }      }
6831      redo B;      next B;
6832      } continue { # B
6833        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
6834          ## NOTE: The code below is executed in cases where it does not have
6835          ## to be, but it it is harmless even in those cases.
6836          ## has an element in scope
6837          INSCOPE: {
6838            for (reverse 0..$#{$self->{open_elements}}) {
6839              my $node = $self->{open_elements}->[$_];
6840              if ($node->[1] & FOREIGN_EL) {
6841                last INSCOPE;
6842              } elsif ($node->[1] & SCOPING_EL) {
6843                last;
6844              }
6845            }
6846            
6847            ## NOTE: No foreign element in scope.
6848            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
6849          } # INSCOPE
6850        }
6851    } # B    } # B
6852    
6853    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6271  sub set_inner_html ($$$) { Line 6893  sub set_inner_html ($$$) {
6893    
6894      ## Step 8 # MUST      ## Step 8 # MUST
6895      my $i = 0;      my $i = 0;
6896      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
6897      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
6898      $p->{set_next_char} = sub {      $p->{set_next_char} = sub {
6899        my $self = shift;        my $self = shift;
6900    
# Line 6281  sub set_inner_html ($$$) { Line 6903  sub set_inner_html ($$$) {
6903    
6904        $self->{next_char} = -1 and return if $i >= length $$s;        $self->{next_char} = -1 and return if $i >= length $$s;
6905        $self->{next_char} = ord substr $$s, $i++, 1;        $self->{next_char} = ord substr $$s, $i++, 1;
6906        $column++;  
6907          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
6908          $p->{column}++;
6909    
6910        if ($self->{next_char} == 0x000A) { # LF        if ($self->{next_char} == 0x000A) { # LF
6911          $line++;          $p->{line}++;
6912          $column = 0;          $p->{column} = 0;
6913          !!!cp ('i1');          !!!cp ('i1');
6914        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{next_char} == 0x000D) { # CR
6915          $i++ if substr ($$s, $i, 1) eq "\x0A";          $i++ if substr ($$s, $i, 1) eq "\x0A";
6916          $self->{next_char} = 0x000A; # LF # MUST          $self->{next_char} = 0x000A; # LF # MUST
6917          $line++;          $p->{line}++;
6918          $column = 0;          $p->{column} = 0;
6919          !!!cp ('i2');          !!!cp ('i2');
6920        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{next_char} > 0x10FFFF) {
6921          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
# Line 6307  sub set_inner_html ($$$) { Line 6931  sub set_inner_html ($$$) {
6931            
6932      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
6933        my (%opt) = @_;        my (%opt) = @_;
6934        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
6935          my $column = $opt{column};
6936          if (defined $opt{token} and defined $opt{token}->{line}) {
6937            $line = $opt{token}->{line};
6938            $column = $opt{token}->{column};
6939          }
6940          warn "Parse error ($opt{type}) at line $line column $column\n";
6941      };      };
6942      $p->{parse_error} = sub {      $p->{parse_error} = sub {
6943        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
6944      };      };
6945            
6946      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
# Line 6334  sub set_inner_html ($$$) { Line 6964  sub set_inner_html ($$$) {
6964          unless defined $p->{content_model};          unless defined $p->{content_model};
6965          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
6966    
6967      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
6968          ## TODO: Foreign element OK?
6969    
6970      ## Step 3      ## Step 3
6971      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6344  sub set_inner_html ($$$) { Line 6975  sub set_inner_html ($$$) {
6975      $doc->append_child ($root);      $doc->append_child ($root);
6976    
6977      ## Step 5 # MUST      ## Step 5 # MUST
6978      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
6979    
6980      undef $p->{head_element};      undef $p->{head_element};
6981    
# Line 6390  sub set_inner_html ($$$) { Line 7021  sub set_inner_html ($$$) {
7021      ## ISSUE: mutation events?      ## ISSUE: mutation events?
7022    
7023      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
7024    
7025        delete $p->{parse_error}; # delete loop
7026    } else {    } else {
7027      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";
7028    }    }

Legend:
Removed from v.1.86  
changed lines
  Added in v.1.130

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24