/[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.53 by wakaba, Sat Jul 21 12:37:57 2007 UTC revision 1.59 by wakaba, Sat Sep 8 01:31:44 2007 UTC
# Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_ Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_
159  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
160  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
161    
162    sub DATA_STATE () { 0 }
163    sub ENTITY_DATA_STATE () { 1 }
164    sub TAG_OPEN_STATE () { 2 }
165    sub CLOSE_TAG_OPEN_STATE () { 3 }
166    sub TAG_NAME_STATE () { 4 }
167    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
168    sub ATTRIBUTE_NAME_STATE () { 6 }
169    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
170    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
171    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
172    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
173    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
174    sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
175    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
176    sub COMMENT_START_STATE () { 14 }
177    sub COMMENT_START_DASH_STATE () { 15 }
178    sub COMMENT_STATE () { 16 }
179    sub COMMENT_END_STATE () { 17 }
180    sub COMMENT_END_DASH_STATE () { 18 }
181    sub BOGUS_COMMENT_STATE () { 19 }
182    sub DOCTYPE_STATE () { 20 }
183    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
184    sub DOCTYPE_NAME_STATE () { 22 }
185    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
186    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
187    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
188    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
189    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
190    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
191    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
192    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
193    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
194    sub BOGUS_DOCTYPE_STATE () { 32 }
195    
196    sub DOCTYPE_TOKEN () { 1 }
197    sub COMMENT_TOKEN () { 2 }
198    sub START_TAG_TOKEN () { 3 }
199    sub END_TAG_TOKEN () { 4 }
200    sub END_OF_FILE_TOKEN () { 5 }
201    sub CHARACTER_TOKEN () { 6 }
202    
203    sub AFTER_HTML_IMS () { 0b100 }
204    sub HEAD_IMS ()       { 0b1000 }
205    sub BODY_IMS ()       { 0b10000 }
206    sub BODY_TABLE_IMS () { 0b100000 }
207    sub TABLE_IMS ()      { 0b1000000 }
208    sub ROW_IMS ()        { 0b10000000 }
209    sub BODY_AFTER_IMS () { 0b100000000 }
210    sub FRAME_IMS ()      { 0b1000000000 }
211    
212    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
213    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
214    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
215    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
216    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
217    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
218    sub IN_BODY_IM () { BODY_IMS }
219    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
220    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
221    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
222    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
223    sub IN_TABLE_IM () { TABLE_IMS }
224    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
225    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
226    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
227    sub IN_SELECT_IM () { 0b01 }
228    sub IN_COLUMN_GROUP_IM () { 0b10 }
229    
230  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
231    
232  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
233    my $self = shift;    my $self = shift;
234    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
235    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
236    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE
237    undef $self->{current_attribute};    undef $self->{current_attribute};
# Line 177  sub _initialize_tokenizer ($) { Line 245  sub _initialize_tokenizer ($) {
245  } # _initialize_tokenizer  } # _initialize_tokenizer
246    
247  ## A token has:  ## A token has:
248  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
249  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
250  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
251  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
252  ##   ->{system_identifier} (DOCTYPE)  ##   ->{public_identifier} (DOCTYPE_TOKEN)
253  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{system_identifier} (DOCTYPE_TOKEN)
254  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)
255  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
256    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
257    
258  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
259    
# Line 194  sub _initialize_tokenizer ($) { Line 263  sub _initialize_tokenizer ($) {
263  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
264  ## and removed from the list.  ## and removed from the list.
265    
266    ## NOTE: HTML5 "Writing HTML documents" section, applied to
267    ## documents and not to user agents and conformance checkers,
268    ## contains some requirements that are not detected by the
269    ## parsing algorithm:
270    ## - Some requirements on character encoding declarations. ## TODO
271    ## - "Elements MUST NOT contain content that their content model disallows."
272    ##   ... Some are parse error, some are not (will be reported by c.c.).
273    ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO
274    ## - Text (in elements, attributes, and comments) SHOULD NOT contain
275    ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)
276    
277    ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot
278    ## be detected by the HTML5 parsing algorithm:
279    ## - Text,
280    
281  sub _get_next_token ($) {  sub _get_next_token ($) {
282    my $self = shift;    my $self = shift;
283    if (@{$self->{token}}) {    if (@{$self->{token}}) {
# Line 201  sub _get_next_token ($) { Line 285  sub _get_next_token ($) {
285    }    }
286    
287    A: {    A: {
288      if ($self->{state} eq 'data') {      if ($self->{state} == DATA_STATE) {
289        if ($self->{next_input_character} == 0x0026) { # &        if ($self->{next_input_character} == 0x0026) { # &
290          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA
291            $self->{state} = 'entity data';            $self->{state} = ENTITY_DATA_STATE;
292            !!!next-input-character;            !!!next-input-character;
293            redo A;            redo A;
294          } else {          } else {
# Line 226  sub _get_next_token ($) { Line 310  sub _get_next_token ($) {
310          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
311              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
312               not $self->{escape})) {               not $self->{escape})) {
313            $self->{state} = 'tag open';            $self->{state} = TAG_OPEN_STATE;
314            !!!next-input-character;            !!!next-input-character;
315            redo A;            redo A;
316          } else {          } else {
# Line 243  sub _get_next_token ($) { Line 327  sub _get_next_token ($) {
327                    
328          #          #
329        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
330          !!!emit ({type => 'end-of-file'});          !!!emit ({type => END_OF_FILE_TOKEN});
331          last A; ## TODO: ok?          last A; ## TODO: ok?
332        }        }
333        # Anything else        # Anything else
334        my $token = {type => 'character',        my $token = {type => CHARACTER_TOKEN,
335                     data => chr $self->{next_input_character}};                     data => chr $self->{next_input_character}};
336        ## Stay in the data state        ## Stay in the data state
337        !!!next-input-character;        !!!next-input-character;
# Line 255  sub _get_next_token ($) { Line 339  sub _get_next_token ($) {
339        !!!emit ($token);        !!!emit ($token);
340    
341        redo A;        redo A;
342      } elsif ($self->{state} eq 'entity data') {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
343        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
344                
345        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);
346    
347        $self->{state} = 'data';        $self->{state} = DATA_STATE;
348        # next-input-character is already done        # next-input-character is already done
349    
350        unless (defined $token) {        unless (defined $token) {
351          !!!emit ({type => 'character', data => '&'});          !!!emit ({type => CHARACTER_TOKEN, data => '&'});
352        } else {        } else {
353          !!!emit ($token);          !!!emit ($token);
354        }        }
355    
356        redo A;        redo A;
357      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
358        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
359          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{next_input_character} == 0x002F) { # /
360            !!!next-input-character;            !!!next-input-character;
361            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
362            redo A;            redo A;
363          } else {          } else {
364            ## reconsume            ## reconsume
365            $self->{state} = 'data';            $self->{state} = DATA_STATE;
366    
367            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<'});
368    
369            redo A;            redo A;
370          }          }
371        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
372          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{next_input_character} == 0x0021) { # !
373            $self->{state} = 'markup declaration open';            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
374            !!!next-input-character;            !!!next-input-character;
375            redo A;            redo A;
376          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{next_input_character} == 0x002F) { # /
377            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
378            !!!next-input-character;            !!!next-input-character;
379            redo A;            redo A;
380          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_input_character} and
381                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{next_input_character} <= 0x005A) { # A..Z
382            $self->{current_token}            $self->{current_token}
383              = {type => 'start tag',              = {type => START_TAG_TOKEN,
384                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{next_input_character} + 0x0020)};
385            $self->{state} = 'tag name';            $self->{state} = TAG_NAME_STATE;
386            !!!next-input-character;            !!!next-input-character;
387            redo A;            redo A;
388          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_input_character} and
389                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{next_input_character} <= 0x007A) { # a..z
390            $self->{current_token} = {type => 'start tag',            $self->{current_token} = {type => START_TAG_TOKEN,
391                              tag_name => chr ($self->{next_input_character})};                              tag_name => chr ($self->{next_input_character})};
392            $self->{state} = 'tag name';            $self->{state} = TAG_NAME_STATE;
393            !!!next-input-character;            !!!next-input-character;
394            redo A;            redo A;
395          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_input_character} == 0x003E) { # >
396            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag');
397            $self->{state} = 'data';            $self->{state} = DATA_STATE;
398            !!!next-input-character;            !!!next-input-character;
399    
400            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});
401    
402            redo A;            redo A;
403          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_input_character} == 0x003F) { # ?
404            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio');
405            $self->{state} = 'bogus comment';            $self->{state} = BOGUS_COMMENT_STATE;
406            ## $self->{next_input_character} is intentionally left as is            ## $self->{next_input_character} is intentionally left as is
407            redo A;            redo A;
408          } else {          } else {
409            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago');
410            $self->{state} = 'data';            $self->{state} = DATA_STATE;
411            ## reconsume            ## reconsume
412    
413            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<'});
414    
415            redo A;            redo A;
416          }          }
417        } else {        } else {
418          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
419        }        }
420      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
421        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
422          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_emitted_start_tag_name}) {
423            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>
# Line 348  sub _get_next_token ($) { Line 432  sub _get_next_token ($) {
432              } else {              } else {
433                $self->{next_input_character} = shift @next_char; # reconsume                $self->{next_input_character} = shift @next_char; # reconsume
434                !!!back-next-input-character (@next_char);                !!!back-next-input-character (@next_char);
435                $self->{state} = 'data';                $self->{state} = DATA_STATE;
436    
437                !!!emit ({type => 'character', data => '</'});                !!!emit ({type => CHARACTER_TOKEN, data => '</'});
438        
439                redo A;                redo A;
440              }              }
# Line 367  sub _get_next_token ($) { Line 451  sub _get_next_token ($) {
451                    $self->{next_input_character} == -1) {                    $self->{next_input_character} == -1) {
452              $self->{next_input_character} = shift @next_char; # reconsume              $self->{next_input_character} = shift @next_char; # reconsume
453              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
454              $self->{state} = 'data';              $self->{state} = DATA_STATE;
455              !!!emit ({type => 'character', data => '</'});              !!!emit ({type => CHARACTER_TOKEN, data => '</'});
456              redo A;              redo A;
457            } else {            } else {
458              $self->{next_input_character} = shift @next_char;              $self->{next_input_character} = shift @next_char;
# Line 378  sub _get_next_token ($) { Line 462  sub _get_next_token ($) {
462          } else {          } else {
463            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
464            # next-input-character is already done            # next-input-character is already done
465            $self->{state} = 'data';            $self->{state} = DATA_STATE;
466            !!!emit ({type => 'character', data => '</'});            !!!emit ({type => CHARACTER_TOKEN, data => '</'});
467            redo A;            redo A;
468          }          }
469        }        }
470                
471        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{next_input_character} and
472            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{next_input_character} <= 0x005A) { # A..Z
473          $self->{current_token} = {type => 'end tag',          $self->{current_token} = {type => END_TAG_TOKEN,
474                            tag_name => chr ($self->{next_input_character} + 0x0020)};                            tag_name => chr ($self->{next_input_character} + 0x0020)};
475          $self->{state} = 'tag name';          $self->{state} = TAG_NAME_STATE;
476          !!!next-input-character;          !!!next-input-character;
477          redo A;          redo A;
478        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{next_input_character} and
479                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{next_input_character} <= 0x007A) { # a..z
480          $self->{current_token} = {type => 'end tag',          $self->{current_token} = {type => END_TAG_TOKEN,
481                            tag_name => chr ($self->{next_input_character})};                            tag_name => chr ($self->{next_input_character})};
482          $self->{state} = 'tag name';          $self->{state} = TAG_NAME_STATE;
483          !!!next-input-character;          !!!next-input-character;
484          redo A;          redo A;
485        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
486          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag');
487          $self->{state} = 'data';          $self->{state} = DATA_STATE;
488          !!!next-input-character;          !!!next-input-character;
489          redo A;          redo A;
490        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
491          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
492          $self->{state} = 'data';          $self->{state} = DATA_STATE;
493          # reconsume          # reconsume
494    
495          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</'});
496    
497          redo A;          redo A;
498        } else {        } else {
499          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
500          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
501          ## $self->{next_input_character} is intentionally left as is          ## $self->{next_input_character} is intentionally left as is
502          redo A;          redo A;
503        }        }
504      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
505        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
506            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
507            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
508            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
509            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
510          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
511          !!!next-input-character;          !!!next-input-character;
512          redo A;          redo A;
513        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
514          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
515            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
516                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
517            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
518          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
519            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
520            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
521              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 439  sub _get_next_token ($) { Line 523  sub _get_next_token ($) {
523          } else {          } else {
524            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
525          }          }
526          $self->{state} = 'data';          $self->{state} = DATA_STATE;
527          !!!next-input-character;          !!!next-input-character;
528    
529          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 454  sub _get_next_token ($) { Line 538  sub _get_next_token ($) {
538          redo A;          redo A;
539        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
540          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
541          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
542            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
543                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
544            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
545          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
546            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
547            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
548              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 466  sub _get_next_token ($) { Line 550  sub _get_next_token ($) {
550          } else {          } else {
551            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
552          }          }
553          $self->{state} = 'data';          $self->{state} = DATA_STATE;
554          # reconsume          # reconsume
555    
556          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 475  sub _get_next_token ($) { Line 559  sub _get_next_token ($) {
559        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
560          !!!next-input-character;          !!!next-input-character;
561          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
562              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
563              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
564            # permitted slash            # permitted slash
565            #            #
566          } else {          } else {
567            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
568          }          }
569          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
570          # next-input-character is already done          # next-input-character is already done
571          redo A;          redo A;
572        } else {        } else {
# Line 492  sub _get_next_token ($) { Line 576  sub _get_next_token ($) {
576          !!!next-input-character;          !!!next-input-character;
577          redo A;          redo A;
578        }        }
579      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
580        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
581            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
582            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 502  sub _get_next_token ($) { Line 586  sub _get_next_token ($) {
586          !!!next-input-character;          !!!next-input-character;
587          redo A;          redo A;
588        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
589          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
590            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
591                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
592            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
593          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
594            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
595            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
596              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 514  sub _get_next_token ($) { Line 598  sub _get_next_token ($) {
598          } else {          } else {
599            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
600          }          }
601          $self->{state} = 'data';          $self->{state} = DATA_STATE;
602          !!!next-input-character;          !!!next-input-character;
603    
604          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 524  sub _get_next_token ($) { Line 608  sub _get_next_token ($) {
608                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_input_character} <= 0x005A) { # A..Z
609          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),
610                                value => ''};                                value => ''};
611          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
612          !!!next-input-character;          !!!next-input-character;
613          redo A;          redo A;
614        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
615          !!!next-input-character;          !!!next-input-character;
616          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
617              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
618              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
619            # permitted slash            # permitted slash
620            #            #
# Line 542  sub _get_next_token ($) { Line 626  sub _get_next_token ($) {
626          redo A;          redo A;
627        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
628          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
629          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
630            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
631                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
632            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
633          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
634            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
635            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 554  sub _get_next_token ($) { Line 638  sub _get_next_token ($) {
638          } else {          } else {
639            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
640          }          }
641          $self->{state} = 'data';          $self->{state} = DATA_STATE;
642          # reconsume          # reconsume
643    
644          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 563  sub _get_next_token ($) { Line 647  sub _get_next_token ($) {
647        } else {        } else {
648          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{current_attribute} = {name => chr ($self->{next_input_character}),
649                                value => ''};                                value => ''};
650          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
651          !!!next-input-character;          !!!next-input-character;
652          redo A;          redo A;
653        }        }
654      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
655        my $before_leave = sub {        my $before_leave = sub {
656          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
657              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
# Line 585  sub _get_next_token ($) { Line 669  sub _get_next_token ($) {
669            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
670            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
671          $before_leave->();          $before_leave->();
672          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
673          !!!next-input-character;          !!!next-input-character;
674          redo A;          redo A;
675        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_input_character} == 0x003D) { # =
676          $before_leave->();          $before_leave->();
677          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
678          !!!next-input-character;          !!!next-input-character;
679          redo A;          redo A;
680        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
681          $before_leave->();          $before_leave->();
682          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
683            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
684                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
685            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
686          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
687            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
688            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
689              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 607  sub _get_next_token ($) { Line 691  sub _get_next_token ($) {
691          } else {          } else {
692            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
693          }          }
694          $self->{state} = 'data';          $self->{state} = DATA_STATE;
695          !!!next-input-character;          !!!next-input-character;
696    
697          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 623  sub _get_next_token ($) { Line 707  sub _get_next_token ($) {
707          $before_leave->();          $before_leave->();
708          !!!next-input-character;          !!!next-input-character;
709          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
710              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
711              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
712            # permitted slash            # permitted slash
713            #            #
714          } else {          } else {
715            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
716          }          }
717          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
718          # next-input-character is already done          # next-input-character is already done
719          redo A;          redo A;
720        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
721          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
722          $before_leave->();          $before_leave->();
723          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
724            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
725                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
726            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
727          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
728            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
729            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
730              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 648  sub _get_next_token ($) { Line 732  sub _get_next_token ($) {
732          } else {          } else {
733            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
734          }          }
735          $self->{state} = 'data';          $self->{state} = DATA_STATE;
736          # reconsume          # reconsume
737    
738          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 660  sub _get_next_token ($) { Line 744  sub _get_next_token ($) {
744          !!!next-input-character;          !!!next-input-character;
745          redo A;          redo A;
746        }        }
747      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
748        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
749            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
750            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 670  sub _get_next_token ($) { Line 754  sub _get_next_token ($) {
754          !!!next-input-character;          !!!next-input-character;
755          redo A;          redo A;
756        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_input_character} == 0x003D) { # =
757          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
758          !!!next-input-character;          !!!next-input-character;
759          redo A;          redo A;
760        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
761          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
762            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
763                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
764            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
765          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
766            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
767            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
768              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 686  sub _get_next_token ($) { Line 770  sub _get_next_token ($) {
770          } else {          } else {
771            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
772          }          }
773          $self->{state} = 'data';          $self->{state} = DATA_STATE;
774          !!!next-input-character;          !!!next-input-character;
775    
776          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 696  sub _get_next_token ($) { Line 780  sub _get_next_token ($) {
780                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_input_character} <= 0x005A) { # A..Z
781          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),
782                                value => ''};                                value => ''};
783          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
784          !!!next-input-character;          !!!next-input-character;
785          redo A;          redo A;
786        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_input_character} == 0x002F) { # /
787          !!!next-input-character;          !!!next-input-character;
788          if ($self->{next_input_character} == 0x003E and # >          if ($self->{next_input_character} == 0x003E and # >
789              $self->{current_token}->{type} eq 'start tag' and              $self->{current_token}->{type} == START_TAG_TOKEN and
790              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {
791            # permitted slash            # permitted slash
792            #            #
# Line 710  sub _get_next_token ($) { Line 794  sub _get_next_token ($) {
794            !!!parse-error (type => 'nestc');            !!!parse-error (type => 'nestc');
795            ## TODO: Different error type for <aa / bb> than <aa/>            ## TODO: Different error type for <aa / bb> than <aa/>
796          }          }
797          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
798          # next-input-character is already done          # next-input-character is already done
799          redo A;          redo A;
800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
801          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
802          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
803            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
804                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
805            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
806          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
807            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
808            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
809              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 727  sub _get_next_token ($) { Line 811  sub _get_next_token ($) {
811          } else {          } else {
812            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
813          }          }
814          $self->{state} = 'data';          $self->{state} = DATA_STATE;
815          # reconsume          # reconsume
816    
817          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 736  sub _get_next_token ($) { Line 820  sub _get_next_token ($) {
820        } else {        } else {
821          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{current_attribute} = {name => chr ($self->{next_input_character}),
822                                value => ''};                                value => ''};
823          $self->{state} = 'attribute name';          $self->{state} = ATTRIBUTE_NAME_STATE;
824          !!!next-input-character;          !!!next-input-character;
825          redo A;                  redo A;        
826        }        }
827      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
828        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
829            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
830            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 750  sub _get_next_token ($) { Line 834  sub _get_next_token ($) {
834          !!!next-input-character;          !!!next-input-character;
835          redo A;          redo A;
836        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
837          $self->{state} = 'attribute value (double-quoted)';          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
838          !!!next-input-character;          !!!next-input-character;
839          redo A;          redo A;
840        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
841          $self->{state} = 'attribute value (unquoted)';          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
842          ## reconsume          ## reconsume
843          redo A;          redo A;
844        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
845          $self->{state} = 'attribute value (single-quoted)';          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
846          !!!next-input-character;          !!!next-input-character;
847          redo A;          redo A;
848        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
849          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
850            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
851                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
852            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
853          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
854            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
855            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
856              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 774  sub _get_next_token ($) { Line 858  sub _get_next_token ($) {
858          } else {          } else {
859            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
860          }          }
861          $self->{state} = 'data';          $self->{state} = DATA_STATE;
862          !!!next-input-character;          !!!next-input-character;
863    
864          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 782  sub _get_next_token ($) { Line 866  sub _get_next_token ($) {
866          redo A;          redo A;
867        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
868          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
869          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
870            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
871                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
872            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
873          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
874            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
875            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
876              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 794  sub _get_next_token ($) { Line 878  sub _get_next_token ($) {
878          } else {          } else {
879            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
880          }          }
881          $self->{state} = 'data';          $self->{state} = DATA_STATE;
882          ## reconsume          ## reconsume
883    
884          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 802  sub _get_next_token ($) { Line 886  sub _get_next_token ($) {
886          redo A;          redo A;
887        } else {        } else {
888          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});
889          $self->{state} = 'attribute value (unquoted)';          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
890          !!!next-input-character;          !!!next-input-character;
891          redo A;          redo A;
892        }        }
893      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
894        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
895          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
896          !!!next-input-character;          !!!next-input-character;
897          redo A;          redo A;
898        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
899          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          $self->{last_attribute_value_state} = $self->{state};
900          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
901          !!!next-input-character;          !!!next-input-character;
902          redo A;          redo A;
903        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
904          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
905          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
906            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
907                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
908            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
909          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
910            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
911            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
912              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 830  sub _get_next_token ($) { Line 914  sub _get_next_token ($) {
914          } else {          } else {
915            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
916          }          }
917          $self->{state} = 'data';          $self->{state} = DATA_STATE;
918          ## reconsume          ## reconsume
919    
920          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 842  sub _get_next_token ($) { Line 926  sub _get_next_token ($) {
926          !!!next-input-character;          !!!next-input-character;
927          redo A;          redo A;
928        }        }
929      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
930        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
931          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
932          !!!next-input-character;          !!!next-input-character;
933          redo A;          redo A;
934        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
935          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';          $self->{last_attribute_value_state} = $self->{state};
936          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
937          !!!next-input-character;          !!!next-input-character;
938          redo A;          redo A;
939        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
940          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
941          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
942            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
943                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
944            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
945          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
946            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
947            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 866  sub _get_next_token ($) { Line 950  sub _get_next_token ($) {
950          } else {          } else {
951            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
952          }          }
953          $self->{state} = 'data';          $self->{state} = DATA_STATE;
954          ## reconsume          ## reconsume
955    
956          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 878  sub _get_next_token ($) { Line 962  sub _get_next_token ($) {
962          !!!next-input-character;          !!!next-input-character;
963          redo A;          redo A;
964        }        }
965      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
966        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
967            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
968            $self->{next_input_character} == 0x000B or # HT            $self->{next_input_character} == 0x000B or # HT
969            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
970            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
971          $self->{state} = 'before attribute name';          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
972          !!!next-input-character;          !!!next-input-character;
973          redo A;          redo A;
974        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_input_character} == 0x0026) { # &
975          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          $self->{last_attribute_value_state} = $self->{state};
976          $self->{state} = 'entity in attribute value';          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
977          !!!next-input-character;          !!!next-input-character;
978          redo A;          redo A;
979        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
980          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
981            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
982                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
983            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
984          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
985            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
986            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
987              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 905  sub _get_next_token ($) { Line 989  sub _get_next_token ($) {
989          } else {          } else {
990            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
991          }          }
992          $self->{state} = 'data';          $self->{state} = DATA_STATE;
993          !!!next-input-character;          !!!next-input-character;
994    
995          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 913  sub _get_next_token ($) { Line 997  sub _get_next_token ($) {
997          redo A;          redo A;
998        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
999          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1000          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1001            $self->{current_token}->{first_start_tag}            $self->{current_token}->{first_start_tag}
1002                = not defined $self->{last_emitted_start_tag_name};                = not defined $self->{last_emitted_start_tag_name};
1003            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1004          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1005            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1006            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1007              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
# Line 925  sub _get_next_token ($) { Line 1009  sub _get_next_token ($) {
1009          } else {          } else {
1010            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1011          }          }
1012          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1013          ## reconsume          ## reconsume
1014    
1015          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
# Line 937  sub _get_next_token ($) { Line 1021  sub _get_next_token ($) {
1021          !!!next-input-character;          !!!next-input-character;
1022          redo A;          redo A;
1023        }        }
1024      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {
1025        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);
1026    
1027        unless (defined $token) {        unless (defined $token) {
# Line 950  sub _get_next_token ($) { Line 1034  sub _get_next_token ($) {
1034        $self->{state} = $self->{last_attribute_value_state};        $self->{state} = $self->{last_attribute_value_state};
1035        # next-input-character is already done        # next-input-character is already done
1036        redo A;        redo A;
1037      } elsif ($self->{state} eq 'bogus comment') {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1038        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1039                
1040        my $token = {type => 'comment', data => ''};        my $token = {type => COMMENT_TOKEN, data => ''};
1041    
1042        BC: {        BC: {
1043          if ($self->{next_input_character} == 0x003E) { # >          if ($self->{next_input_character} == 0x003E) { # >
1044            $self->{state} = 'data';            $self->{state} = DATA_STATE;
1045            !!!next-input-character;            !!!next-input-character;
1046    
1047            !!!emit ($token);            !!!emit ($token);
1048    
1049            redo A;            redo A;
1050          } elsif ($self->{next_input_character} == -1) {          } elsif ($self->{next_input_character} == -1) {
1051            $self->{state} = 'data';            $self->{state} = DATA_STATE;
1052            ## reconsume            ## reconsume
1053    
1054            !!!emit ($token);            !!!emit ($token);
# Line 976  sub _get_next_token ($) { Line 1060  sub _get_next_token ($) {
1060            redo BC;            redo BC;
1061          }          }
1062        } # BC        } # BC
1063      } elsif ($self->{state} eq 'markup declaration open') {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
1064        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1065    
1066        my @next_char;        my @next_char;
# Line 986  sub _get_next_token ($) { Line 1070  sub _get_next_token ($) {
1070          !!!next-input-character;          !!!next-input-character;
1071          push @next_char, $self->{next_input_character};          push @next_char, $self->{next_input_character};
1072          if ($self->{next_input_character} == 0x002D) { # -          if ($self->{next_input_character} == 0x002D) { # -
1073            $self->{current_token} = {type => 'comment', data => ''};            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};
1074            $self->{state} = 'comment start';            $self->{state} = COMMENT_START_STATE;
1075            !!!next-input-character;            !!!next-input-character;
1076            redo A;            redo A;
1077          }          }
# Line 1018  sub _get_next_token ($) { Line 1102  sub _get_next_token ($) {
1102                    if ($self->{next_input_character} == 0x0045 or # E                    if ($self->{next_input_character} == 0x0045 or # E
1103                        $self->{next_input_character} == 0x0065) { # e                        $self->{next_input_character} == 0x0065) { # e
1104                      ## ISSUE: What a stupid code this is!                      ## ISSUE: What a stupid code this is!
1105                      $self->{state} = 'DOCTYPE';                      $self->{state} = DOCTYPE_STATE;
1106                      !!!next-input-character;                      !!!next-input-character;
1107                      redo A;                      redo A;
1108                    }                    }
# Line 1032  sub _get_next_token ($) { Line 1116  sub _get_next_token ($) {
1116        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment');
1117        $self->{next_input_character} = shift @next_char;        $self->{next_input_character} = shift @next_char;
1118        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1119        $self->{state} = 'bogus comment';        $self->{state} = BOGUS_COMMENT_STATE;
1120        redo A;        redo A;
1121                
1122        ## ISSUE: typos in spec: chacacters, is is a parse error        ## ISSUE: typos in spec: chacacters, is is a parse error
1123        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?
1124      } elsif ($self->{state} eq 'comment start') {      } elsif ($self->{state} == COMMENT_START_STATE) {
1125        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1126          $self->{state} = 'comment start dash';          $self->{state} = COMMENT_START_DASH_STATE;
1127          !!!next-input-character;          !!!next-input-character;
1128          redo A;          redo A;
1129        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1130          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1131          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1132          !!!next-input-character;          !!!next-input-character;
1133    
1134          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1052  sub _get_next_token ($) { Line 1136  sub _get_next_token ($) {
1136          redo A;          redo A;
1137        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1138          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1139          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1140          ## reconsume          ## reconsume
1141    
1142          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1061  sub _get_next_token ($) { Line 1145  sub _get_next_token ($) {
1145        } else {        } else {
1146          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1147              .= chr ($self->{next_input_character});              .= chr ($self->{next_input_character});
1148          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1149          !!!next-input-character;          !!!next-input-character;
1150          redo A;          redo A;
1151        }        }
1152      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
1153        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1154          $self->{state} = 'comment end';          $self->{state} = COMMENT_END_STATE;
1155          !!!next-input-character;          !!!next-input-character;
1156          redo A;          redo A;
1157        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1158          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
1159          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1160          !!!next-input-character;          !!!next-input-character;
1161    
1162          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1080  sub _get_next_token ($) { Line 1164  sub _get_next_token ($) {
1164          redo A;          redo A;
1165        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1166          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1167          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1168          ## reconsume          ## reconsume
1169    
1170          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1089  sub _get_next_token ($) { Line 1173  sub _get_next_token ($) {
1173        } else {        } else {
1174          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
1175              .= '-' . chr ($self->{next_input_character});              .= '-' . chr ($self->{next_input_character});
1176          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1177          !!!next-input-character;          !!!next-input-character;
1178          redo A;          redo A;
1179        }        }
1180      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
1181        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1182          $self->{state} = 'comment end dash';          $self->{state} = COMMENT_END_DASH_STATE;
1183          !!!next-input-character;          !!!next-input-character;
1184          redo A;          redo A;
1185        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1186          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1187          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1188          ## reconsume          ## reconsume
1189    
1190          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1112  sub _get_next_token ($) { Line 1196  sub _get_next_token ($) {
1196          !!!next-input-character;          !!!next-input-character;
1197          redo A;          redo A;
1198        }        }
1199      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
1200        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_input_character} == 0x002D) { # -
1201          $self->{state} = 'comment end';          $self->{state} = COMMENT_END_STATE;
1202          !!!next-input-character;          !!!next-input-character;
1203          redo A;          redo A;
1204        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1205          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1206          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1207          ## reconsume          ## reconsume
1208    
1209          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1127  sub _get_next_token ($) { Line 1211  sub _get_next_token ($) {
1211          redo A;          redo A;
1212        } else {        } else {
1213          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment
1214          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1215          !!!next-input-character;          !!!next-input-character;
1216          redo A;          redo A;
1217        }        }
1218      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
1219        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_input_character} == 0x003E) { # >
1220          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1221          !!!next-input-character;          !!!next-input-character;
1222    
1223          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1147  sub _get_next_token ($) { Line 1231  sub _get_next_token ($) {
1231          redo A;          redo A;
1232        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1233          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
1234          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1235          ## reconsume          ## reconsume
1236    
1237          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
# Line 1156  sub _get_next_token ($) { Line 1240  sub _get_next_token ($) {
1240        } else {        } else {
1241          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment');
1242          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment
1243          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
1244          !!!next-input-character;          !!!next-input-character;
1245          redo A;          redo A;
1246        }        }
1247      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
1248        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1249            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1250            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
1251            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
1252            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
1253          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1254          !!!next-input-character;          !!!next-input-character;
1255          redo A;          redo A;
1256        } else {        } else {
1257          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
1258          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
1259          ## reconsume          ## reconsume
1260          redo A;          redo A;
1261        }        }
1262      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
1263        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1264            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1265            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 1186  sub _get_next_token ($) { Line 1270  sub _get_next_token ($) {
1270          redo A;          redo A;
1271        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1272          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1273          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1274          !!!next-input-character;          !!!next-input-character;
1275    
1276          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect
1277    
1278          redo A;          redo A;
1279        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1280          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
1281          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1282          ## reconsume          ## reconsume
1283    
1284          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect
1285    
1286          redo A;          redo A;
1287        } else {        } else {
1288          $self->{current_token}          $self->{current_token}
1289              = {type => 'DOCTYPE',              = {type => DOCTYPE_TOKEN,
1290                 name => chr ($self->{next_input_character}),                 name => chr ($self->{next_input_character}),
1291                 correct => 1};                 correct => 1};
1292  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
1293          $self->{state} = 'DOCTYPE name';          $self->{state} = DOCTYPE_NAME_STATE;
1294          !!!next-input-character;          !!!next-input-character;
1295          redo A;          redo A;
1296        }        }
1297      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
1298  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
1299        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1300            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1301            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
1302            $self->{next_input_character} == 0x000C or # FF            $self->{next_input_character} == 0x000C or # FF
1303            $self->{next_input_character} == 0x0020) { # SP            $self->{next_input_character} == 0x0020) { # SP
1304          $self->{state} = 'after DOCTYPE name';          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
1305          !!!next-input-character;          !!!next-input-character;
1306          redo A;          redo A;
1307        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1308          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1309          !!!next-input-character;          !!!next-input-character;
1310    
1311          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1229  sub _get_next_token ($) { Line 1313  sub _get_next_token ($) {
1313          redo A;          redo A;
1314        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1315          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1316          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1317          ## reconsume          ## reconsume
1318    
1319          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1243  sub _get_next_token ($) { Line 1327  sub _get_next_token ($) {
1327          !!!next-input-character;          !!!next-input-character;
1328          redo A;          redo A;
1329        }        }
1330      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
1331        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_input_character} == 0x0009 or # HT
1332            $self->{next_input_character} == 0x000A or # LF            $self->{next_input_character} == 0x000A or # LF
1333            $self->{next_input_character} == 0x000B or # VT            $self->{next_input_character} == 0x000B or # VT
# Line 1253  sub _get_next_token ($) { Line 1337  sub _get_next_token ($) {
1337          !!!next-input-character;          !!!next-input-character;
1338          redo A;          redo A;
1339        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1340          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1341          !!!next-input-character;          !!!next-input-character;
1342    
1343          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1261  sub _get_next_token ($) { Line 1345  sub _get_next_token ($) {
1345          redo A;          redo A;
1346        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1347          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1348          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1349          ## reconsume          ## reconsume
1350    
1351          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1285  sub _get_next_token ($) { Line 1369  sub _get_next_token ($) {
1369                  !!!next-input-character;                  !!!next-input-character;
1370                  if ($self->{next_input_character} == 0x0043 or # C                  if ($self->{next_input_character} == 0x0043 or # C
1371                      $self->{next_input_character} == 0x0063) { # c                      $self->{next_input_character} == 0x0063) { # c
1372                    $self->{state} = 'before DOCTYPE public identifier';                    $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1373                    !!!next-input-character;                    !!!next-input-character;
1374                    redo A;                    redo A;
1375                  }                  }
# Line 1312  sub _get_next_token ($) { Line 1396  sub _get_next_token ($) {
1396                  !!!next-input-character;                  !!!next-input-character;
1397                  if ($self->{next_input_character} == 0x004D or # M                  if ($self->{next_input_character} == 0x004D or # M
1398                      $self->{next_input_character} == 0x006D) { # m                      $self->{next_input_character} == 0x006D) { # m
1399                    $self->{state} = 'before DOCTYPE system identifier';                    $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1400                    !!!next-input-character;                    !!!next-input-character;
1401                    redo A;                    redo A;
1402                  }                  }
# Line 1328  sub _get_next_token ($) { Line 1412  sub _get_next_token ($) {
1412        }        }
1413    
1414        !!!parse-error (type => 'string after DOCTYPE name');        !!!parse-error (type => 'string after DOCTYPE name');
1415        $self->{state} = 'bogus DOCTYPE';        $self->{state} = BOGUS_DOCTYPE_STATE;
1416        # next-input-character is already done        # next-input-character is already done
1417        redo A;        redo A;
1418      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
1419        if ({        if ({
1420              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1421              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1341  sub _get_next_token ($) { Line 1425  sub _get_next_token ($) {
1425          redo A;          redo A;
1426        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{next_input_character} eq 0x0022) { # "
1427          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1428          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
1429          !!!next-input-character;          !!!next-input-character;
1430          redo A;          redo A;
1431        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{next_input_character} eq 0x0027) { # '
1432          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
1433          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
1434          !!!next-input-character;          !!!next-input-character;
1435          redo A;          redo A;
1436        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{next_input_character} eq 0x003E) { # >
1437          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
1438    
1439          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1440          !!!next-input-character;          !!!next-input-character;
1441    
1442          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1362  sub _get_next_token ($) { Line 1446  sub _get_next_token ($) {
1446        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1447          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1448    
1449          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1450          ## reconsume          ## reconsume
1451    
1452          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1371  sub _get_next_token ($) { Line 1455  sub _get_next_token ($) {
1455          redo A;          redo A;
1456        } else {        } else {
1457          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
1458          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1459          !!!next-input-character;          !!!next-input-character;
1460          redo A;          redo A;
1461        }        }
1462      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
1463        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
1464          $self->{state} = 'after DOCTYPE public identifier';          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1465          !!!next-input-character;          !!!next-input-character;
1466          redo A;          redo A;
1467        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1468          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
1469    
1470          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1471          ## reconsume          ## reconsume
1472    
1473          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1397  sub _get_next_token ($) { Line 1481  sub _get_next_token ($) {
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        }        }
1484      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
1485        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
1486          $self->{state} = 'after DOCTYPE public identifier';          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
1487          !!!next-input-character;          !!!next-input-character;
1488          redo A;          redo A;
1489        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1490          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
1491    
1492          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1493          ## reconsume          ## reconsume
1494    
1495          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1419  sub _get_next_token ($) { Line 1503  sub _get_next_token ($) {
1503          !!!next-input-character;          !!!next-input-character;
1504          redo A;          redo A;
1505        }        }
1506      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
1507        if ({        if ({
1508              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1509              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1429  sub _get_next_token ($) { Line 1513  sub _get_next_token ($) {
1513          redo A;          redo A;
1514        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
1515          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1516          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
1517          !!!next-input-character;          !!!next-input-character;
1518          redo A;          redo A;
1519        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
1520          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1521          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
1522          !!!next-input-character;          !!!next-input-character;
1523          redo A;          redo A;
1524        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1525          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1526          !!!next-input-character;          !!!next-input-character;
1527    
1528          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1447  sub _get_next_token ($) { Line 1531  sub _get_next_token ($) {
1531        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1532          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1533    
1534          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1535          ## reconsume          ## reconsume
1536    
1537          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1456  sub _get_next_token ($) { Line 1540  sub _get_next_token ($) {
1540          redo A;          redo A;
1541        } else {        } else {
1542          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
1543          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1544          !!!next-input-character;          !!!next-input-character;
1545          redo A;          redo A;
1546        }        }
1547      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
1548        if ({        if ({
1549              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1550              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1470  sub _get_next_token ($) { Line 1554  sub _get_next_token ($) {
1554          redo A;          redo A;
1555        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_input_character} == 0x0022) { # "
1556          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1557          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
1558          !!!next-input-character;          !!!next-input-character;
1559          redo A;          redo A;
1560        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_input_character} == 0x0027) { # '
1561          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
1562          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
1563          !!!next-input-character;          !!!next-input-character;
1564          redo A;          redo A;
1565        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1566          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
1567          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1568          !!!next-input-character;          !!!next-input-character;
1569    
1570          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1490  sub _get_next_token ($) { Line 1574  sub _get_next_token ($) {
1574        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1575          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1576    
1577          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1578          ## reconsume          ## reconsume
1579    
1580          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1499  sub _get_next_token ($) { Line 1583  sub _get_next_token ($) {
1583          redo A;          redo A;
1584        } else {        } else {
1585          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
1586          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1587          !!!next-input-character;          !!!next-input-character;
1588          redo A;          redo A;
1589        }        }
1590      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
1591        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_input_character} == 0x0022) { # "
1592          $self->{state} = 'after DOCTYPE system identifier';          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1593          !!!next-input-character;          !!!next-input-character;
1594          redo A;          redo A;
1595        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1596          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
1597    
1598          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1599          ## reconsume          ## reconsume
1600    
1601          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1525  sub _get_next_token ($) { Line 1609  sub _get_next_token ($) {
1609          !!!next-input-character;          !!!next-input-character;
1610          redo A;          redo A;
1611        }        }
1612      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
1613        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_input_character} == 0x0027) { # '
1614          $self->{state} = 'after DOCTYPE system identifier';          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
1615          !!!next-input-character;          !!!next-input-character;
1616          redo A;          redo A;
1617        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1618          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
1619    
1620          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1621          ## reconsume          ## reconsume
1622    
1623          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1547  sub _get_next_token ($) { Line 1631  sub _get_next_token ($) {
1631          !!!next-input-character;          !!!next-input-character;
1632          redo A;          redo A;
1633        }        }
1634      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
1635        if ({        if ({
1636              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
1637              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
# Line 1556  sub _get_next_token ($) { Line 1640  sub _get_next_token ($) {
1640          !!!next-input-character;          !!!next-input-character;
1641          redo A;          redo A;
1642        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1643          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1644          !!!next-input-character;          !!!next-input-character;
1645    
1646          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
# Line 1565  sub _get_next_token ($) { Line 1649  sub _get_next_token ($) {
1649        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1650          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1651    
1652          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1653          ## reconsume          ## reconsume
1654    
1655          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1574  sub _get_next_token ($) { Line 1658  sub _get_next_token ($) {
1658          redo A;          redo A;
1659        } else {        } else {
1660          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
1661          $self->{state} = 'bogus DOCTYPE';          $self->{state} = BOGUS_DOCTYPE_STATE;
1662          !!!next-input-character;          !!!next-input-character;
1663          redo A;          redo A;
1664        }        }
1665      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
1666        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_input_character} == 0x003E) { # >
1667          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1668          !!!next-input-character;          !!!next-input-character;
1669    
1670          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1589  sub _get_next_token ($) { Line 1673  sub _get_next_token ($) {
1673          redo A;          redo A;
1674        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1675          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
1676          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1677          ## reconsume          ## reconsume
1678    
1679          delete $self->{current_token}->{correct};          delete $self->{current_token}->{correct};
# Line 1670  sub _tokenize_attempt_to_consume_an_enti Line 1754  sub _tokenize_attempt_to_consume_an_enti
1754            $code = $c1_entity_char->{$code};            $code = $c1_entity_char->{$code};
1755          }          }
1756    
1757          return {type => 'character', data => chr $code};          return {type => CHARACTER_TOKEN, data => chr $code};
1758        } # X        } # X
1759      } elsif (0x0030 <= $self->{next_input_character} and      } elsif (0x0030 <= $self->{next_input_character} and
1760               $self->{next_input_character} <= 0x0039) { # 0..9               $self->{next_input_character} <= 0x0039) { # 0..9
# Line 1705  sub _tokenize_attempt_to_consume_an_enti Line 1789  sub _tokenize_attempt_to_consume_an_enti
1789          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
1790        }        }
1791                
1792        return {type => 'character', data => chr $code};        return {type => CHARACTER_TOKEN, data => chr $code};
1793      } else {      } else {
1794        !!!parse-error (type => 'bare nero');        !!!parse-error (type => 'bare nero');
1795        !!!back-next-input-character ($self->{next_input_character});        !!!back-next-input-character ($self->{next_input_character});
# Line 1753  sub _tokenize_attempt_to_consume_an_enti Line 1837  sub _tokenize_attempt_to_consume_an_enti
1837      }      }
1838            
1839      if ($match > 0) {      if ($match > 0) {
1840        return {type => 'character', data => $value};        return {type => CHARACTER_TOKEN, data => $value};
1841      } elsif ($match < 0) {      } elsif ($match < 0) {
1842        !!!parse-error (type => 'no refc');        !!!parse-error (type => 'no refc');
1843        if ($in_attr and $match < -1) {        if ($in_attr and $match < -1) {
1844          return {type => 'character', data => '&'.$entity_name};          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};
1845        } else {        } else {
1846          return {type => 'character', data => $value};          return {type => CHARACTER_TOKEN, data => $value};
1847        }        }
1848      } else {      } else {
1849        !!!parse-error (type => 'bare ero');        !!!parse-error (type => 'bare ero');
1850        ## NOTE: No characters are consumed in the spec.        ## NOTE: No characters are consumed in the spec.
1851        return {type => 'character', data => '&'.$value};        return {type => CHARACTER_TOKEN, data => '&'.$value};
1852      }      }
1853    } else {    } else {
1854      ## no characters are consumed      ## no characters are consumed
# Line 1806  sub _construct_tree ($) { Line 1890  sub _construct_tree ($) {
1890        
1891    !!!next-token;    !!!next-token;
1892    
1893    $self->{insertion_mode} = 'before head';    $self->{insertion_mode} = BEFORE_HEAD_IM;
1894    undef $self->{form_element};    undef $self->{form_element};
1895    undef $self->{head_element};    undef $self->{head_element};
1896    $self->{open_elements} = [];    $self->{open_elements} = [];
# Line 1820  sub _construct_tree ($) { Line 1904  sub _construct_tree ($) {
1904  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
1905    my $self = shift;    my $self = shift;
1906    INITIAL: {    INITIAL: {
1907      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
1908        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
1909        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
1910        ## language.        ## language.
# Line 1947  sub _tree_construction_initial ($) { Line 2031  sub _tree_construction_initial ($) {
2031        !!!next-token;        !!!next-token;
2032        return;        return;
2033      } elsif ({      } elsif ({
2034                'start tag' => 1,                START_TAG_TOKEN, 1,
2035                'end tag' => 1,                END_TAG_TOKEN, 1,
2036                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
2037               }->{$token->{type}}) {               }->{$token->{type}}) {
2038        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE');
2039        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
2040        ## Go to the root element phase        ## Go to the root element phase
2041        ## reprocess        ## reprocess
2042        return;        return;
2043      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
2044        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2045          ## Ignore the token          ## Ignore the token
2046    
# Line 1972  sub _tree_construction_initial ($) { Line 2056  sub _tree_construction_initial ($) {
2056        ## Go to the root element phase        ## Go to the root element phase
2057        ## reprocess        ## reprocess
2058        return;        return;
2059      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
2060        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
2061        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
2062                
# Line 1980  sub _tree_construction_initial ($) { Line 2064  sub _tree_construction_initial ($) {
2064        !!!next-token;        !!!next-token;
2065        redo INITIAL;        redo INITIAL;
2066      } else {      } else {
2067        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
2068      }      }
2069    } # INITIAL    } # INITIAL
2070  } # _tree_construction_initial  } # _tree_construction_initial
# Line 1989  sub _tree_construction_root_element ($) Line 2073  sub _tree_construction_root_element ($)
2073    my $self = shift;    my $self = shift;
2074        
2075    B: {    B: {
2076        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
2077          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE');
2078          ## Ignore the token          ## Ignore the token
2079          ## Stay in the phase          ## Stay in the phase
2080          !!!next-token;          !!!next-token;
2081          redo B;          redo B;
2082        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
2083          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
2084          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
2085          ## Stay in the phase          ## Stay in the phase
2086          !!!next-token;          !!!next-token;
2087          redo B;          redo B;
2088        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
2089          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
2090            ## Ignore the token.            ## Ignore the token.
2091    
# Line 2013  sub _tree_construction_root_element ($) Line 2097  sub _tree_construction_root_element ($)
2097          }          }
2098          #          #
2099        } elsif ({        } elsif ({
2100                  'start tag' => 1,                  START_TAG_TOKEN, 1,
2101                  'end tag' => 1,                  END_TAG_TOKEN, 1,
2102                  'end-of-file' => 1,                  END_OF_FILE_TOKEN, 1,
2103                 }->{$token->{type}}) {                 }->{$token->{type}}) {
2104          ## ISSUE: There is an issue in the spec          ## ISSUE: There is an issue in the spec
2105          #          #
2106        } else {        } else {
2107          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
2108        }        }
2109        my $root_element; !!!create-element ($root_element, 'html');        my $root_element; !!!create-element ($root_element, 'html');
2110        $self->{document}->append_child ($root_element);        $self->{document}->append_child ($root_element);
# Line 2062  sub _reset_insertion_mode ($) { Line 2146  sub _reset_insertion_mode ($) {
2146            
2147        ## Step 4..13        ## Step 4..13
2148        my $new_mode = {        my $new_mode = {
2149                        select => 'in select',                        select => IN_SELECT_IM,
2150                        td => 'in cell',                        td => IN_CELL_IM,
2151                        th => 'in cell',                        th => IN_CELL_IM,
2152                        tr => 'in row',                        tr => IN_ROW_IM,
2153                        tbody => 'in table body',                        tbody => IN_TABLE_BODY_IM,
2154                        thead => 'in table body',                        thead => IN_TABLE_BODY_IM,
2155                        tfoot => 'in table body',                        tfoot => IN_TABLE_BODY_IM,
2156                        caption => 'in caption',                        caption => IN_CAPTION_IM,
2157                        colgroup => 'in column group',                        colgroup => IN_COLUMN_GROUP_IM,
2158                        table => 'in table',                        table => IN_TABLE_IM,
2159                        head => 'in body', # not in head!                        head => IN_BODY_IM, # not in head!
2160                        body => 'in body',                        body => IN_BODY_IM,
2161                        frameset => 'in frameset',                        frameset => IN_FRAMESET_IM,
2162                       }->{$node->[1]};                       }->{$node->[1]};
2163        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
2164                
2165        ## Step 14        ## Step 14
2166        if ($node->[1] eq 'html') {        if ($node->[1] eq 'html') {
2167          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
2168            $self->{insertion_mode} = 'before head';            $self->{insertion_mode} = BEFORE_HEAD_IM;
2169          } else {          } else {
2170            $self->{insertion_mode} = 'after head';            $self->{insertion_mode} = AFTER_HEAD_IM;
2171          }          }
2172          return;          return;
2173        }        }
2174                
2175        ## Step 15        ## Step 15
2176        $self->{insertion_mode} = 'in body' and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
2177                
2178        ## Step 16        ## Step 16
2179        $i--;        $i--;
# Line 2203  sub _tree_construction_main ($) { Line 2287  sub _tree_construction_main ($) {
2287      ## Step 4      ## Step 4
2288      my $text = '';      my $text = '';
2289      !!!next-token;      !!!next-token;
2290      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
2291        $text .= $token->{data};        $text .= $token->{data};
2292        !!!next-token;        !!!next-token;
2293      }      }
# Line 2218  sub _tree_construction_main ($) { Line 2302  sub _tree_construction_main ($) {
2302      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
2303    
2304      ## Step 7      ## Step 7
2305      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {
2306        ## Ignore the token        ## Ignore the token
2307      } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {      } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {
2308        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!parse-error (type => 'in CDATA:#'.$token->{type});
# Line 2241  sub _tree_construction_main ($) { Line 2325  sub _tree_construction_main ($) {
2325            
2326      my $text = '';      my $text = '';
2327      !!!next-token;      !!!next-token;
2328      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
2329        $text .= $token->{data};        $text .= $token->{data};
2330        !!!next-token;        !!!next-token;
2331      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
# Line 2251  sub _tree_construction_main ($) { Line 2335  sub _tree_construction_main ($) {
2335                                
2336      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
2337    
2338      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
2339          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
2340        ## Ignore the token        ## Ignore the token
2341      } else {      } else {
# Line 2497  sub _tree_construction_main ($) { Line 2581  sub _tree_construction_main ($) {
2581    my $insert;    my $insert;
2582    
2583    B: {    B: {
2584      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
2585        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'DOCTYPE in the middle');
2586        ## Ignore the token        ## Ignore the token
2587        ## Stay in the phase        ## Stay in the phase
2588        !!!next-token;        !!!next-token;
2589        redo B;        redo B;
2590      } elsif ($token->{type} eq 'end-of-file') {      } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2591        if ($self->{insertion_mode} eq 'after html body' or        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
           $self->{insertion_mode} eq 'after html frameset') {  
2592          #          #
2593        } else {        } else {
2594          ## Generate implied end tags          ## Generate implied end tags
# Line 2514  sub _tree_construction_main ($) { Line 2597  sub _tree_construction_main ($) {
2597               tbody => 1, tfoot=> 1, thead => 1,               tbody => 1, tfoot=> 1, thead => 1,
2598              }->{$self->{open_elements}->[-1]->[1]}) {              }->{$self->{open_elements}->[-1]->[1]}) {
2599            !!!back-token;            !!!back-token;
2600            $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};            $token = {type => END_TAG_TOKEN, tag_name => $self->{open_elements}->[-1]->[1]};
2601            redo B;            redo B;
2602          }          }
2603                    
# Line 2532  sub _tree_construction_main ($) { Line 2615  sub _tree_construction_main ($) {
2615    
2616        ## Stop parsing        ## Stop parsing
2617        last B;        last B;
2618      } elsif ($token->{type} eq 'start tag' and      } elsif ($token->{type} == START_TAG_TOKEN and
2619               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
2620        if ($self->{insertion_mode} eq 'after html body') {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
2621          ## Turn into the main phase          ## Turn into the main phase
2622          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html');
2623          $self->{insertion_mode} = 'after body';          $self->{insertion_mode} = AFTER_BODY_IM;
2624        } elsif ($self->{insertion_mode} eq 'after html frameset') {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
2625          ## Turn into the main phase          ## Turn into the main phase
2626          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html:html');
2627          $self->{insertion_mode} = 'after frameset';          $self->{insertion_mode} = AFTER_FRAMESET_IM;
2628        }        }
2629    
2630  ## ISSUE: "aa<html>" is not a parse error.  ## ISSUE: "aa<html>" is not a parse error.
# Line 2559  sub _tree_construction_main ($) { Line 2642  sub _tree_construction_main ($) {
2642        }        }
2643        !!!next-token;        !!!next-token;
2644        redo B;        redo B;
2645      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
2646        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
2647        if ($self->{insertion_mode} eq 'after html body' or        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
           $self->{insertion_mode} eq 'after html frameset') {  
2648          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
2649        } elsif ($self->{insertion_mode} eq 'after body') {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
2650          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
2651        } else {        } else {
2652          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
2653        }        }
2654        !!!next-token;        !!!next-token;
2655        redo B;        redo B;
2656      } elsif ($self->{insertion_mode} eq 'in head' or      } elsif ($self->{insertion_mode} & HEAD_IMS) {
2657               $self->{insertion_mode} eq 'in head noscript' or        if ($token->{type} == CHARACTER_TOKEN) {
              $self->{insertion_mode} eq 'after head' or  
              $self->{insertion_mode} eq 'before head') {  
       if ($token->{type} eq 'character') {  
2658          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
2659            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2660            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2584  sub _tree_construction_main ($) { Line 2663  sub _tree_construction_main ($) {
2663            }            }
2664          }          }
2665    
2666          if ($self->{insertion_mode} eq 'before head') {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2667            ## As if <head>            ## As if <head>
2668            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, 'head');
2669            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
# Line 2594  sub _tree_construction_main ($) { Line 2673  sub _tree_construction_main ($) {
2673            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
2674    
2675            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
2676          } elsif ($self->{insertion_mode} eq 'in head noscript') {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2677            ## As if </noscript>            ## As if </noscript>
2678            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
2679            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#character');
# Line 2604  sub _tree_construction_main ($) { Line 2683  sub _tree_construction_main ($) {
2683            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
2684    
2685            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
2686          } elsif ($self->{insertion_mode} eq 'in head') {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2687            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
2688    
2689            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
# Line 2613  sub _tree_construction_main ($) { Line 2692  sub _tree_construction_main ($) {
2692              ## "after head" insertion mode              ## "after head" insertion mode
2693              ## As if <body>              ## As if <body>
2694              !!!insert-element ('body');              !!!insert-element ('body');
2695              $self->{insertion_mode} = 'in body';              $self->{insertion_mode} = IN_BODY_IM;
2696              ## reprocess              ## reprocess
2697              redo B;              redo B;
2698            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
2699              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
2700                if ($self->{insertion_mode} eq 'before head') {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2701                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});
2702                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2703                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];
2704                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2705                  !!!next-token;                  !!!next-token;
2706                  redo B;                  redo B;
2707                } elsif ($self->{insertion_mode} ne 'after head') {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2708                    #
2709                  } else {
2710                  !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!parse-error (type => 'in head:head'); # or in head noscript
2711                  ## Ignore the token                  ## Ignore the token
2712                  !!!next-token;                  !!!next-token;
2713                  redo B;                  redo B;
               } else {  
                 #  
2714                }                }
2715              } elsif ($self->{insertion_mode} eq 'before head') {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2716                ## As if <head>                ## As if <head>
2717                !!!create-element ($self->{head_element}, 'head');                !!!create-element ($self->{head_element}, 'head');
2718                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2719                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2720    
2721                $self->{insertion_mode} = 'in head';                $self->{insertion_mode} = IN_HEAD_IM;
2722                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
2723              }              }
2724    
2725              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
2726                if ($self->{insertion_mode} eq 'in head noscript') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2727                  ## As if </noscript>                  ## As if </noscript>
2728                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2729                  !!!parse-error (type => 'in noscript:base');                  !!!parse-error (type => 'in noscript:base');
2730                                
2731                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2732                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2733                }                }
2734    
2735                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
2736                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2737                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2738                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2739                }                }
2740                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
2741                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
2742                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2743                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2744                !!!next-token;                !!!next-token;
2745                redo B;                redo B;
2746              } elsif ($token->{tag_name} eq 'link') {              } elsif ($token->{tag_name} eq 'link') {
2747                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
2748                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2749                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2750                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2751                }                }
2752                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
2753                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
2754                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2755                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2756                !!!next-token;                !!!next-token;
2757                redo B;                redo B;
2758              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
2759                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
2760                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2761                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2762                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2763                }                }
# Line 2704  sub _tree_construction_main ($) { Line 2783  sub _tree_construction_main ($) {
2783    
2784                ## TODO: Extracting |charset| from |meta|.                ## TODO: Extracting |charset| from |meta|.
2785                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2786                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2787                !!!next-token;                !!!next-token;
2788                redo B;                redo B;
2789              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
2790                if ($self->{insertion_mode} eq 'in head noscript') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2791                  ## As if </noscript>                  ## As if </noscript>
2792                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2793                  !!!parse-error (type => 'in noscript:title');                  !!!parse-error (type => 'in noscript:title');
2794                                
2795                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2796                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2797                } elsif ($self->{insertion_mode} eq 'after head') {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2798                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2799                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2800                }                }
# Line 2726  sub _tree_construction_main ($) { Line 2805  sub _tree_construction_main ($) {
2805                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL,
2806                                sub { $parent->append_child ($_[0]) });                                sub { $parent->append_child ($_[0]) });
2807                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2808                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2809                redo B;                redo B;
2810              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style') {
2811                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2812                ## insertion mode 'in head')                ## insertion mode IN_HEAD_IM)
2813                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
2814                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2815                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2816                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2817                }                }
2818                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);
2819                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2820                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2821                redo B;                redo B;
2822              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
2823                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2824                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
2825                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes});
2826                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
2827                  !!!next-token;                  !!!next-token;
2828                  redo B;                  redo B;
2829                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2830                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript:noscript');
2831                  ## Ignore the token                  ## Ignore the token
2832                  !!!next-token;                  !!!next-token;
# Line 2756  sub _tree_construction_main ($) { Line 2835  sub _tree_construction_main ($) {
2835                  #                  #
2836                }                }
2837              } elsif ($token->{tag_name} eq 'script') {              } elsif ($token->{tag_name} eq 'script') {
2838                if ($self->{insertion_mode} eq 'in head noscript') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2839                  ## As if </noscript>                  ## As if </noscript>
2840                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2841                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:script');
2842                                
2843                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2844                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2845                } elsif ($self->{insertion_mode} eq 'after head') {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2846                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head:'.$token->{tag_name});
2847                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2848                }                }
# Line 2771  sub _tree_construction_main ($) { Line 2850  sub _tree_construction_main ($) {
2850                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
2851                $script_start_tag->($insert_to_current);                $script_start_tag->($insert_to_current);
2852                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
2853                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2854                redo B;                redo B;
2855              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
2856                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
2857                if ($self->{insertion_mode} eq 'in head noscript') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2858                  ## As if </noscript>                  ## As if </noscript>
2859                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2860                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});
# Line 2785  sub _tree_construction_main ($) { Line 2864  sub _tree_construction_main ($) {
2864                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2865                                    
2866                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
2867                } elsif ($self->{insertion_mode} eq 'in head') {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2868                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2869                                    
2870                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
# Line 2793  sub _tree_construction_main ($) { Line 2872  sub _tree_construction_main ($) {
2872    
2873                ## "after head" insertion mode                ## "after head" insertion mode
2874                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
2875                $self->{insertion_mode} = 'in '.$token->{tag_name};                if ($token->{tag_name} eq 'body') {
2876                    $self->{insertion_mode} = IN_BODY_IM;
2877                  } elsif ($token->{tag_name} eq 'frameset') {
2878                    $self->{insertion_mode} = IN_FRAMESET_IM;
2879                  } else {
2880                    die "$0: tag name: $self->{tag_name}";
2881                  }
2882                !!!next-token;                !!!next-token;
2883                redo B;                redo B;
2884              } else {              } else {
2885                #                #
2886              }              }
2887    
2888              if ($self->{insertion_mode} eq 'in head noscript') {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2889                ## As if </noscript>                ## As if </noscript>
2890                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
2891                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});
# Line 2810  sub _tree_construction_main ($) { Line 2895  sub _tree_construction_main ($) {
2895                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
2896    
2897                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
2898              } elsif ($self->{insertion_mode} eq 'in head') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2899                ## As if </head>                ## As if </head>
2900                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
2901    
# Line 2820  sub _tree_construction_main ($) { Line 2905  sub _tree_construction_main ($) {
2905              ## "after head" insertion mode              ## "after head" insertion mode
2906              ## As if <body>              ## As if <body>
2907              !!!insert-element ('body');              !!!insert-element ('body');
2908              $self->{insertion_mode} = 'in body';              $self->{insertion_mode} = IN_BODY_IM;
2909              ## reprocess              ## reprocess
2910              redo B;              redo B;
2911            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
2912              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
2913                if ($self->{insertion_mode} eq 'before head') {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2914                  ## As if <head>                  ## As if <head>
2915                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head');
2916                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
# Line 2833  sub _tree_construction_main ($) { Line 2918  sub _tree_construction_main ($) {
2918    
2919                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2920                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2921                  $self->{insertion_mode} = 'after head';                  $self->{insertion_mode} = AFTER_HEAD_IM;
2922                  !!!next-token;                  !!!next-token;
2923                  redo B;                  redo B;
2924                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2925                  ## As if </noscript>                  ## As if </noscript>
2926                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2927                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:script');
2928                                    
2929                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2930                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2931                  $self->{insertion_mode} = 'after head';                  $self->{insertion_mode} = AFTER_HEAD_IM;
2932                  !!!next-token;                  !!!next-token;
2933                  redo B;                  redo B;
2934                } elsif ($self->{insertion_mode} eq 'in head') {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2935                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2936                  $self->{insertion_mode} = 'after head';                  $self->{insertion_mode} = AFTER_HEAD_IM;
2937                  !!!next-token;                  !!!next-token;
2938                  redo B;                  redo B;
2939                } else {                } else {
2940                  #                  #
2941                }                }
2942              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
2943                if ($self->{insertion_mode} eq 'in head noscript') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2944                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2945                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2946                  !!!next-token;                  !!!next-token;
2947                  redo B;                  redo B;
2948                } elsif ($self->{insertion_mode} eq 'before head') {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2949                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag:noscript');
2950                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
2951                  !!!next-token;                  !!!next-token;
# Line 2871  sub _tree_construction_main ($) { Line 2956  sub _tree_construction_main ($) {
2956              } elsif ({              } elsif ({
2957                        body => 1, html => 1,                        body => 1, html => 1,
2958                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2959                if ($self->{insertion_mode} eq 'before head') {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2960                  ## As if <head>                  ## As if <head>
2961                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head');
2962                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2963                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2964    
2965                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2966                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2967                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2968                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2969                  ## Ignore the token                  ## Ignore the token
2970                  !!!next-token;                  !!!next-token;
# Line 2890  sub _tree_construction_main ($) { Line 2975  sub _tree_construction_main ($) {
2975              } elsif ({              } elsif ({
2976                        p => 1, br => 1,                        p => 1, br => 1,
2977                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2978                if ($self->{insertion_mode} eq 'before head') {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2979                  ## As if <head>                  ## As if <head>
2980                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, 'head');
2981                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2982                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2983    
2984                  $self->{insertion_mode} = 'in head';                  $self->{insertion_mode} = IN_HEAD_IM;
2985                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
2986                }                }
2987    
2988                #                #
2989              } else {              } else {
2990                if ($self->{insertion_mode} ne 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2991                    #
2992                  } else {
2993                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2994                  ## Ignore the token                  ## Ignore the token
2995                  !!!next-token;                  !!!next-token;
2996                  redo B;                  redo B;
               } else {  
                 #  
2997                }                }
2998              }              }
2999    
3000              if ($self->{insertion_mode} eq 'in head noscript') {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
3001                ## As if </noscript>                ## As if </noscript>
3002                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3003                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});
# Line 2922  sub _tree_construction_main ($) { Line 3007  sub _tree_construction_main ($) {
3007                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3008    
3009                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
3010              } elsif ($self->{insertion_mode} eq 'in head') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
3011                ## As if </head>                ## As if </head>
3012                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3013    
3014                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
3015              } elsif ($self->{insertion_mode} eq 'before head') {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
3016                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3017                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
3018                !!!next-token;                !!!next-token;
# Line 2937  sub _tree_construction_main ($) { Line 3022  sub _tree_construction_main ($) {
3022              ## "after head" insertion mode              ## "after head" insertion mode
3023              ## As if <body>              ## As if <body>
3024              !!!insert-element ('body');              !!!insert-element ('body');
3025              $self->{insertion_mode} = 'in body';              $self->{insertion_mode} = IN_BODY_IM;
3026              ## reprocess              ## reprocess
3027              redo B;              redo B;
3028            } else {            } else {
# Line 2945  sub _tree_construction_main ($) { Line 3030  sub _tree_construction_main ($) {
3030            }            }
3031    
3032            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
3033      } elsif ($self->{insertion_mode} eq 'in body' or      } elsif ($self->{insertion_mode} & BODY_IMS) {
3034               $self->{insertion_mode} eq 'in cell' or            if ($token->{type} == CHARACTER_TOKEN) {
              $self->{insertion_mode} eq 'in caption') {  
           if ($token->{type} eq 'character') {  
3035              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
3036              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
3037                            
# Line 2956  sub _tree_construction_main ($) { Line 3039  sub _tree_construction_main ($) {
3039    
3040              !!!next-token;              !!!next-token;
3041              redo B;              redo B;
3042            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
3043              if ({              if ({
3044                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
3045                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
3046                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3047                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
3048                  ## have an element in table scope                  ## have an element in table scope
3049                  my $tn;                  my $tn;
3050                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 2984  sub _tree_construction_main ($) { Line 3067  sub _tree_construction_main ($) {
3067                                    
3068                  ## Close the cell                  ## Close the cell
3069                  !!!back-token; # <?>                  !!!back-token; # <?>
3070                  $token = {type => 'end tag', tag_name => $tn};                  $token = {type => END_TAG_TOKEN, tag_name => $tn};
3071                  redo B;                  redo B;
3072                } elsif ($self->{insertion_mode} eq 'in caption') {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
3073                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed:caption');
3074                                    
3075                  ## As if </caption>                  ## As if </caption>
# Line 3017  sub _tree_construction_main ($) { Line 3100  sub _tree_construction_main ($) {
3100                       tbody => 1, tfoot=> 1, thead => 1,                       tbody => 1, tfoot=> 1, thead => 1,
3101                      }->{$self->{open_elements}->[-1]->[1]}) {                      }->{$self->{open_elements}->[-1]->[1]}) {
3102                    !!!back-token; # <?>                    !!!back-token; # <?>
3103                    $token = {type => 'end tag', tag_name => 'caption'};                    $token = {type => END_TAG_TOKEN, tag_name => 'caption'};
3104                    !!!back-token;                    !!!back-token;
3105                    $token = {type => 'end tag',                    $token = {type => END_TAG_TOKEN,
3106                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3107                    redo B;                    redo B;
3108                  }                  }
# Line 3032  sub _tree_construction_main ($) { Line 3115  sub _tree_construction_main ($) {
3115                                    
3116                  $clear_up_to_marker->();                  $clear_up_to_marker->();
3117                                    
3118                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
3119                                    
3120                  ## reprocess                  ## reprocess
3121                  redo B;                  redo B;
# Line 3042  sub _tree_construction_main ($) { Line 3125  sub _tree_construction_main ($) {
3125              } else {              } else {
3126                #                #
3127              }              }
3128            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
3129              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
3130                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
3131                  ## have an element in table scope                  ## have an element in table scope
3132                  my $i;                  my $i;
3133                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3074  sub _tree_construction_main ($) { Line 3157  sub _tree_construction_main ($) {
3157                       tbody => 1, tfoot=> 1, thead => 1,                       tbody => 1, tfoot=> 1, thead => 1,
3158                      }->{$self->{open_elements}->[-1]->[1]}) {                      }->{$self->{open_elements}->[-1]->[1]}) {
3159                    !!!back-token;                    !!!back-token;
3160                    $token = {type => 'end tag',                    $token = {type => END_TAG_TOKEN,
3161                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3162                    redo B;                    redo B;
3163                  }                  }
# Line 3087  sub _tree_construction_main ($) { Line 3170  sub _tree_construction_main ($) {
3170                                    
3171                  $clear_up_to_marker->();                  $clear_up_to_marker->();
3172                                    
3173                  $self->{insertion_mode} = 'in row';                  $self->{insertion_mode} = IN_ROW_IM;
3174                                    
3175                  !!!next-token;                  !!!next-token;
3176                  redo B;                  redo B;
3177                } elsif ($self->{insertion_mode} eq 'in caption') {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
3178                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3179                  ## Ignore the token                  ## Ignore the token
3180                  !!!next-token;                  !!!next-token;
# Line 3100  sub _tree_construction_main ($) { Line 3183  sub _tree_construction_main ($) {
3183                  #                  #
3184                }                }
3185              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
3186                if ($self->{insertion_mode} eq 'in caption') {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
3187                  ## have a table element in table scope                  ## have a table element in table scope
3188                  my $i;                  my $i;
3189                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3128  sub _tree_construction_main ($) { Line 3211  sub _tree_construction_main ($) {
3211                       tbody => 1, tfoot=> 1, thead => 1,                       tbody => 1, tfoot=> 1, thead => 1,
3212                      }->{$self->{open_elements}->[-1]->[1]}) {                      }->{$self->{open_elements}->[-1]->[1]}) {
3213                    !!!back-token;                    !!!back-token;
3214                    $token = {type => 'end tag',                    $token = {type => END_TAG_TOKEN,
3215                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                              tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3216                    redo B;                    redo B;
3217                  }                  }
# Line 3141  sub _tree_construction_main ($) { Line 3224  sub _tree_construction_main ($) {
3224                                    
3225                  $clear_up_to_marker->();                  $clear_up_to_marker->();
3226                                    
3227                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
3228                                    
3229                  !!!next-token;                  !!!next-token;
3230                  redo B;                  redo B;
3231                } elsif ($self->{insertion_mode} eq 'in cell') {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
3232                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3233                  ## Ignore the token                  ## Ignore the token
3234                  !!!next-token;                  !!!next-token;
# Line 3157  sub _tree_construction_main ($) { Line 3240  sub _tree_construction_main ($) {
3240                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
3241                        thead => 1, tr => 1,                        thead => 1, tr => 1,
3242                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3243                       $self->{insertion_mode} eq 'in cell') {                       $self->{insertion_mode} == IN_CELL_IM) {
3244                ## have an element in table scope                ## have an element in table scope
3245                my $i;                my $i;
3246                my $tn;                my $tn;
# Line 3185  sub _tree_construction_main ($) { Line 3268  sub _tree_construction_main ($) {
3268    
3269                ## Close the cell                ## Close the cell
3270                !!!back-token; # </?>                !!!back-token; # </?>
3271                $token = {type => 'end tag', tag_name => $tn};                $token = {type => END_TAG_TOKEN, tag_name => $tn};
3272                redo B;                redo B;
3273              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
3274                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
3275                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed:caption');
3276    
3277                ## As if </caption>                ## As if </caption>
# Line 3219  sub _tree_construction_main ($) { Line 3302  sub _tree_construction_main ($) {
3302                     tbody => 1, tfoot=> 1, thead => 1,                     tbody => 1, tfoot=> 1, thead => 1,
3303                    }->{$self->{open_elements}->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3304                  !!!back-token; # </table>                  !!!back-token; # </table>
3305                  $token = {type => 'end tag', tag_name => 'caption'};                  $token = {type => END_TAG_TOKEN, tag_name => 'caption'};
3306                  !!!back-token;                  !!!back-token;
3307                  $token = {type => 'end tag',                  $token = {type => END_TAG_TOKEN,
3308                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3309                  redo B;                  redo B;
3310                }                }
# Line 3234  sub _tree_construction_main ($) { Line 3317  sub _tree_construction_main ($) {
3317    
3318                $clear_up_to_marker->();                $clear_up_to_marker->();
3319    
3320                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
3321    
3322                ## reprocess                ## reprocess
3323                redo B;                redo B;
3324              } elsif ({              } elsif ({
3325                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
3326                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3327                if ($self->{insertion_mode} eq 'in cell' or                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
                   $self->{insertion_mode} eq 'in caption') {  
3328                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3329                  ## Ignore the token                  ## Ignore the token
3330                  !!!next-token;                  !!!next-token;
# Line 3254  sub _tree_construction_main ($) { Line 3336  sub _tree_construction_main ($) {
3336                        tbody => 1, tfoot => 1,                        tbody => 1, tfoot => 1,
3337                        thead => 1, tr => 1,                        thead => 1, tr => 1,
3338                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3339                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
3340                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3341                ## Ignore the token                ## Ignore the token
3342                !!!next-token;                !!!next-token;
# Line 3268  sub _tree_construction_main ($) { Line 3350  sub _tree_construction_main ($) {
3350    
3351        $insert = $insert_to_current;        $insert = $insert_to_current;
3352        #        #
3353      } elsif ($self->{insertion_mode} eq 'in row' or      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3354               $self->{insertion_mode} eq 'in table body' or        if ($token->{type} == CHARACTER_TOKEN) {
              $self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
3355              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3356                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3357                                
# Line 3329  sub _tree_construction_main ($) { Line 3408  sub _tree_construction_main ($) {
3408                            
3409              !!!next-token;              !!!next-token;
3410              redo B;              redo B;
3411            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
3412              if ({              if ({
3413                   tr => ($self->{insertion_mode} ne 'in row'),                   tr => ($self->{insertion_mode} != IN_ROW_IM),
3414                   th => 1, td => 1,                   th => 1, td => 1,
3415                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3416                if ($self->{insertion_mode} eq 'in table') {                if ($self->{insertion_mode} == IN_TABLE_IM) {
3417                  ## Clear back to table context                  ## Clear back to table context
3418                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while ($self->{open_elements}->[-1]->[1] ne 'table' and
3419                         $self->{open_elements}->[-1]->[1] ne 'html') {                         $self->{open_elements}->[-1]->[1] ne 'html') {
# Line 3343  sub _tree_construction_main ($) { Line 3422  sub _tree_construction_main ($) {
3422                  }                  }
3423                                    
3424                  !!!insert-element ('tbody');                  !!!insert-element ('tbody');
3425                  $self->{insertion_mode} = 'in table body';                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
3426                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3427                }                }
3428    
3429                if ($self->{insertion_mode} eq 'in table body') {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3430                  unless ($token->{tag_name} eq 'tr') {                  unless ($token->{tag_name} eq 'tr') {
3431                    !!!parse-error (type => 'missing start tag:tr');                    !!!parse-error (type => 'missing start tag:tr');
3432                  }                  }
# Line 3360  sub _tree_construction_main ($) { Line 3439  sub _tree_construction_main ($) {
3439                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3440                  }                  }
3441                                    
3442                  $self->{insertion_mode} = 'in row';                  $self->{insertion_mode} = IN_ROW_IM;
3443                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
3444                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!insert-element ($token->{tag_name}, $token->{attributes});
3445                    !!!next-token;                    !!!next-token;
# Line 3380  sub _tree_construction_main ($) { Line 3459  sub _tree_construction_main ($) {
3459                }                }
3460                                
3461                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3462                $self->{insertion_mode} = 'in cell';                $self->{insertion_mode} = IN_CELL_IM;
3463    
3464                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
3465                                
# Line 3389  sub _tree_construction_main ($) { Line 3468  sub _tree_construction_main ($) {
3468              } elsif ({              } elsif ({
3469                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
3470                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3471                        tr => 1, # $self->{insertion_mode} eq 'in row'                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3472                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3473                if ($self->{insertion_mode} eq 'in row') {                if ($self->{insertion_mode} == IN_ROW_IM) {
3474                  ## As if </tr>                  ## As if </tr>
3475                  ## have an element in table scope                  ## have an element in table scope
3476                  my $i;                  my $i;
# Line 3422  sub _tree_construction_main ($) { Line 3501  sub _tree_construction_main ($) {
3501                  }                  }
3502                                    
3503                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
3504                  $self->{insertion_mode} = 'in table body';                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
3505                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
3506                    ## reprocess                    ## reprocess
3507                    redo B;                    redo B;
# Line 3431  sub _tree_construction_main ($) { Line 3510  sub _tree_construction_main ($) {
3510                  }                  }
3511                }                }
3512    
3513                if ($self->{insertion_mode} eq 'in table body') {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3514                  ## have an element in table scope                  ## have an element in table scope
3515                  my $i;                  my $i;
3516                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3470  sub _tree_construction_main ($) { Line 3549  sub _tree_construction_main ($) {
3549                  ## nop by definition                  ## nop by definition
3550                                    
3551                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3552                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
3553                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
3554                }                }
3555    
# Line 3483  sub _tree_construction_main ($) { Line 3562  sub _tree_construction_main ($) {
3562                  }                  }
3563                                    
3564                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup');
3565                  $self->{insertion_mode} = 'in column group';                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3566                  ## reprocess                  ## reprocess
3567                  redo B;                  redo B;
3568                } elsif ({                } elsif ({
# Line 3503  sub _tree_construction_main ($) { Line 3582  sub _tree_construction_main ($) {
3582                                    
3583                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3584                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
3585                                             caption => 'in caption',                                             caption => IN_CAPTION_IM,
3586                                             colgroup => 'in column group',                                             colgroup => IN_COLUMN_GROUP_IM,
3587                                             tbody => 'in table body',                                             tbody => IN_TABLE_BODY_IM,
3588                                             tfoot => 'in table body',                                             tfoot => IN_TABLE_BODY_IM,
3589                                             thead => 'in table body',                                             thead => IN_TABLE_BODY_IM,
3590                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
3591                  !!!next-token;                  !!!next-token;
3592                  redo B;                  redo B;
# Line 3515  sub _tree_construction_main ($) { Line 3594  sub _tree_construction_main ($) {
3594                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
3595                }                }
3596              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
               ## NOTE: There are code clones for this "table in table"  
3597                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3598    
3599                ## As if </table>                ## As if </table>
# Line 3546  sub _tree_construction_main ($) { Line 3624  sub _tree_construction_main ($) {
3624                     tbody => 1, tfoot=> 1, thead => 1,                     tbody => 1, tfoot=> 1, thead => 1,
3625                    }->{$self->{open_elements}->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3626                  !!!back-token; # <table>                  !!!back-token; # <table>
3627                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => END_TAG_TOKEN, tag_name => 'table'};
3628                  !!!back-token;                  !!!back-token;
3629                  $token = {type => 'end tag',                  $token = {type => END_TAG_TOKEN,
3630                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3631                  redo B;                  redo B;
3632                }                }
# Line 3563  sub _tree_construction_main ($) { Line 3641  sub _tree_construction_main ($) {
3641    
3642                ## reprocess                ## reprocess
3643                redo B;                redo B;
3644              } else {          } else {
3645                #            !!!parse-error (type => 'in table:'.$token->{tag_name});
3646              }  
3647            } elsif ($token->{type} eq 'end tag') {            $insert = $insert_to_foster;
3648              #
3649            }
3650          } elsif ($token->{type} == END_TAG_TOKEN) {
3651              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
3652                  $self->{insertion_mode} eq 'in row') {                  $self->{insertion_mode} == IN_ROW_IM) {
3653                ## have an element in table scope                ## have an element in table scope
3654                my $i;                my $i;
3655                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3598  sub _tree_construction_main ($) { Line 3679  sub _tree_construction_main ($) {
3679                }                }
3680    
3681                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
3682                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
3683                !!!next-token;                !!!next-token;
3684                redo B;                redo B;
3685              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3686                if ($self->{insertion_mode} eq 'in row') {                if ($self->{insertion_mode} == IN_ROW_IM) {
3687                  ## As if </tr>                  ## As if </tr>
3688                  ## have an element in table scope                  ## have an element in table scope
3689                  my $i;                  my $i;
# Line 3633  sub _tree_construction_main ($) { Line 3714  sub _tree_construction_main ($) {
3714                  }                  }
3715                                    
3716                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
3717                  $self->{insertion_mode} = 'in table body';                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
3718                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3719                }                }
3720    
3721                if ($self->{insertion_mode} eq 'in table body') {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
3722                  ## have an element in table scope                  ## have an element in table scope
3723                  my $i;                  my $i;
3724                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3676  sub _tree_construction_main ($) { Line 3757  sub _tree_construction_main ($) {
3757                  ## nop by definition                  ## nop by definition
3758                                    
3759                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3760                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
3761                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
3762                }                }
3763    
# Line 3707  sub _tree_construction_main ($) { Line 3788  sub _tree_construction_main ($) {
3788                     tbody => 1, tfoot=> 1, thead => 1,                     tbody => 1, tfoot=> 1, thead => 1,
3789                    }->{$self->{open_elements}->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3790                  !!!back-token;                  !!!back-token;
3791                  $token = {type => 'end tag',                  $token = {type => END_TAG_TOKEN,
3792                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3793                  redo B;                  redo B;
3794                }                }
# Line 3725  sub _tree_construction_main ($) { Line 3806  sub _tree_construction_main ($) {
3806              } elsif ({              } elsif ({
3807                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3808                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3809                       ($self->{insertion_mode} eq 'in row' or                       $self->{insertion_mode} & ROW_IMS) {
3810                        $self->{insertion_mode} eq 'in table body')) {                if ($self->{insertion_mode} == IN_ROW_IM) {
               if ($self->{insertion_mode} eq 'in row') {  
3811                  ## have an element in table scope                  ## have an element in table scope
3812                  my $i;                  my $i;
3813                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 3778  sub _tree_construction_main ($) { Line 3858  sub _tree_construction_main ($) {
3858                  }                  }
3859                                    
3860                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
3861                  $self->{insertion_mode} = 'in table body';                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
3862                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3863                }                }
3864    
# Line 3811  sub _tree_construction_main ($) { Line 3891  sub _tree_construction_main ($) {
3891                }                }
3892    
3893                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3894                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
3895                !!!next-token;                !!!next-token;
3896                redo B;                redo B;
3897              } elsif ({              } elsif ({
3898                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
3899                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
3900                        tr => 1, # $self->{insertion_mode} eq 'in row'                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3901                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} eq 'in table'                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
3902                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3903                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3904                ## Ignore the token                ## Ignore the token
3905                !!!next-token;                !!!next-token;
3906                redo B;                redo B;
3907              } else {          } else {
3908                #            !!!parse-error (type => 'in table:/'.$token->{tag_name});
             }  
           } else {  
             die "$0: $token->{type}: Unknown token type";  
           }  
   
       !!!parse-error (type => 'in table:'.$token->{tag_name});  
3909    
3910        $insert = $insert_to_foster;            $insert = $insert_to_foster;
3911        #            #
3912      } elsif ($self->{insertion_mode} eq 'in column group') {          }
3913            if ($token->{type} eq 'character') {        } else {
3914            die "$0: $token->{type}: Unknown token type";
3915          }
3916        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
3917              if ($token->{type} == CHARACTER_TOKEN) {
3918              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3919                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3920                unless (length $token->{data}) {                unless (length $token->{data}) {
# Line 3846  sub _tree_construction_main ($) { Line 3924  sub _tree_construction_main ($) {
3924              }              }
3925                            
3926              #              #
3927            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
3928              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
3929                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3930                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
# Line 3855  sub _tree_construction_main ($) { Line 3933  sub _tree_construction_main ($) {
3933              } else {              } else {
3934                #                #
3935              }              }
3936            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
3937              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3938                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] eq 'html') {
3939                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag:colgroup');
# Line 3864  sub _tree_construction_main ($) { Line 3942  sub _tree_construction_main ($) {
3942                  redo B;                  redo B;
3943                } else {                } else {
3944                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
3945                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
3946                  !!!next-token;                  !!!next-token;
3947                  redo B;                              redo B;            
3948                }                }
# Line 3888  sub _tree_construction_main ($) { Line 3966  sub _tree_construction_main ($) {
3966              redo B;              redo B;
3967            } else {            } else {
3968              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
3969              $self->{insertion_mode} = 'in table';              $self->{insertion_mode} = IN_TABLE_IM;
3970              ## reprocess              ## reprocess
3971              redo B;              redo B;
3972            }            }
3973      } elsif ($self->{insertion_mode} eq 'in select') {      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {
3974            if ($token->{type} eq 'character') {        if ($token->{type} == CHARACTER_TOKEN) {
3975              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3976              !!!next-token;          !!!next-token;
3977              redo B;          redo B;
3978            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
3979              if ($token->{tag_name} eq 'option') {              if ($token->{tag_name} eq 'option') {
3980                if ($self->{open_elements}->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
3981                  ## As if </option>                  ## As if </option>
# Line 3950  sub _tree_construction_main ($) { Line 4028  sub _tree_construction_main ($) {
4028    
4029                !!!next-token;                !!!next-token;
4030                redo B;                redo B;
4031              } else {          } else {
4032                #            !!!parse-error (type => 'in select:'.$token->{tag_name});
4033              }            ## Ignore the token
4034            } elsif ($token->{type} eq 'end tag') {            !!!next-token;
4035              redo B;
4036            }
4037          } elsif ($token->{type} == END_TAG_TOKEN) {
4038              if ($token->{tag_name} eq 'optgroup') {              if ($token->{tag_name} eq 'optgroup') {
4039                if ($self->{open_elements}->[-1]->[1] eq 'option' and                if ($self->{open_elements}->[-1]->[1] eq 'option' and
4040                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {
# Line 4055  sub _tree_construction_main ($) { Line 4136  sub _tree_construction_main ($) {
4136    
4137                ## reprocess                ## reprocess
4138                redo B;                redo B;
4139              } else {          } else {
4140                #            !!!parse-error (type => 'in select:/'.$token->{tag_name});
             }  
           } else {  
             #  
           }  
   
           !!!parse-error (type => 'in select:'.$token->{tag_name});  
4141            ## Ignore the token            ## Ignore the token
4142            !!!next-token;            !!!next-token;
4143            redo B;            redo B;
4144      } elsif ($self->{insertion_mode} eq 'after body' or          }
4145               $self->{insertion_mode} eq 'after html body') {        } else {
4146        if ($token->{type} eq 'character') {          die "$0: $token->{type}: Unknown token type";
4147          }
4148        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
4149          if ($token->{type} == CHARACTER_TOKEN) {
4150          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4151            my $data = $1;            my $data = $1;
4152            ## As if in body            ## As if in body
# Line 4082  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160            }            }
4161          }          }
4162                    
4163          if ($self->{insertion_mode} eq 'after html body') {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4164            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#character');
4165    
4166            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "main" phase, "after body" insertion mode...
# Line 4091  sub _tree_construction_main ($) { Line 4169  sub _tree_construction_main ($) {
4169          ## "after body" insertion mode          ## "after body" insertion mode
4170          !!!parse-error (type => 'after body:#character');          !!!parse-error (type => 'after body:#character');
4171    
4172          $self->{insertion_mode} = 'in body';          $self->{insertion_mode} = IN_BODY_IM;
4173          ## reprocess          ## reprocess
4174          redo B;          redo B;
4175        } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4176          if ($self->{insertion_mode} eq 'after html body') {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4177            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html:'.$token->{tag_name});
4178                        
4179            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "main" phase, "after body" insertion mode...
# Line 4104  sub _tree_construction_main ($) { Line 4182  sub _tree_construction_main ($) {
4182          ## "after body" insertion mode          ## "after body" insertion mode
4183          !!!parse-error (type => 'after body:'.$token->{tag_name});          !!!parse-error (type => 'after body:'.$token->{tag_name});
4184    
4185          $self->{insertion_mode} = 'in body';          $self->{insertion_mode} = IN_BODY_IM;
4186          ## reprocess          ## reprocess
4187          redo B;          redo B;
4188        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{type} == END_TAG_TOKEN) {
4189          if ($self->{insertion_mode} eq 'after html body') {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4190            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/'.$token->{tag_name});
4191                        
4192            $self->{insertion_mode} = 'after body';            $self->{insertion_mode} = AFTER_BODY_IM;
4193            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess in the "main" phase, "after body" insertion mode...
4194          }          }
4195    
# Line 4123  sub _tree_construction_main ($) { Line 4201  sub _tree_construction_main ($) {
4201              !!!next-token;              !!!next-token;
4202              redo B;              redo B;
4203            } else {            } else {
4204              $self->{insertion_mode} = 'after html body';              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
4205              !!!next-token;              !!!next-token;
4206              redo B;              redo B;
4207            }            }
4208          } else {          } else {
4209            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/'.$token->{tag_name});
4210    
4211            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
4212            ## reprocess            ## reprocess
4213            redo B;            redo B;
4214          }          }
4215        } else {        } else {
4216          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4217        }        }
4218      } elsif ($self->{insertion_mode} eq 'in frameset' or      } elsif ($self->{insertion_mode} & FRAME_IMS) {
4219               $self->{insertion_mode} eq 'after frameset' or        if ($token->{type} == CHARACTER_TOKEN) {
              $self->{insertion_mode} eq 'after html frameset') {  
       if ($token->{type} eq 'character') {  
4220          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4221            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4222                        
# Line 4151  sub _tree_construction_main ($) { Line 4227  sub _tree_construction_main ($) {
4227          }          }
4228                    
4229          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
4230            if ($self->{insertion_mode} eq 'in frameset') {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4231              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#character');
4232            } elsif ($self->{insertion_mode} eq 'after frameset') {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
4233              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#character');
4234            } else { # "after html frameset"            } else { # "after html frameset"
4235              !!!parse-error (type => 'after html:#character');              !!!parse-error (type => 'after html:#character');
4236    
4237              $self->{insertion_mode} = 'after frameset';              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4238              ## Reprocess in the "main" phase, "after frameset"...              ## Reprocess in the "main" phase, "after frameset"...
4239              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#character');
4240            }            }
# Line 4173  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249          }          }
4250                    
4251          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
4252        } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4253          if ($self->{insertion_mode} eq 'after html frameset') {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4254            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html:'.$token->{tag_name});
4255    
4256            $self->{insertion_mode} = 'after frameset';            $self->{insertion_mode} = AFTER_FRAMESET_IM;
4257            ## Process in the "main" phase, "after frameset" insertion mode...            ## Process in the "main" phase, "after frameset" insertion mode...
4258          }          }
4259    
4260          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4261              $self->{insertion_mode} eq 'in frameset') {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4262            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes});
4263            !!!next-token;            !!!next-token;
4264            redo B;            redo B;
4265          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
4266                   $self->{insertion_mode} eq 'in frameset') {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
4267            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes});
4268            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4269            !!!next-token;            !!!next-token;
# Line 4197  sub _tree_construction_main ($) { Line 4273  sub _tree_construction_main ($) {
4273            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);
4274            redo B;            redo B;
4275          } else {          } else {
4276            if ($self->{insertion_mode} eq 'in frameset') {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4277              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset:'.$token->{tag_name});
4278            } else {            } else {
4279              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset:'.$token->{tag_name});
# Line 4206  sub _tree_construction_main ($) { Line 4282  sub _tree_construction_main ($) {
4282            !!!next-token;            !!!next-token;
4283            redo B;            redo B;
4284          }          }
4285        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{type} == END_TAG_TOKEN) {
4286          if ($self->{insertion_mode} eq 'after html frameset') {          if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4287            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/'.$token->{tag_name});
4288    
4289            $self->{insertion_mode} = 'after frameset';            $self->{insertion_mode} = AFTER_FRAMESET_IM;
4290            ## Process in the "main" phase, "after frameset" insertion mode...            ## Process in the "main" phase, "after frameset" insertion mode...
4291          }          }
4292    
4293          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4294              $self->{insertion_mode} eq 'in frameset') {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4295            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] eq 'html' and
4296                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4297              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
# Line 4228  sub _tree_construction_main ($) { Line 4304  sub _tree_construction_main ($) {
4304    
4305            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4306                $self->{open_elements}->[-1]->[1] ne 'frameset') {                $self->{open_elements}->[-1]->[1] ne 'frameset') {
4307              $self->{insertion_mode} = 'after frameset';              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4308            }            }
4309            redo B;            redo B;
4310          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
4311                   $self->{insertion_mode} eq 'after frameset') {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
4312            $self->{insertion_mode} = 'after html frameset';            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
4313            !!!next-token;            !!!next-token;
4314            redo B;            redo B;
4315          } else {          } else {
4316            if ($self->{insertion_mode} eq 'in frameset') {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
4317              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});
4318            } else {            } else {
4319              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});
# Line 4256  sub _tree_construction_main ($) { Line 4332  sub _tree_construction_main ($) {
4332      }      }
4333    
4334      ## "in body" insertion mode      ## "in body" insertion mode
4335      if ($token->{type} eq 'start tag') {      if ($token->{type} == START_TAG_TOKEN) {
4336        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
4337          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
4338          $script_start_tag->($insert);          $script_start_tag->($insert);
# Line 4336  sub _tree_construction_main ($) { Line 4412  sub _tree_construction_main ($) {
4412          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4413            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4414              !!!back-token;              !!!back-token;
4415              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4416              redo B;              redo B;
4417            } elsif ({            } elsif ({
4418                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4349  sub _tree_construction_main ($) { Line 4425  sub _tree_construction_main ($) {
4425          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4426          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre') {
4427            !!!next-token;            !!!next-token;
4428            if ($token->{type} eq 'character') {            if ($token->{type} == CHARACTER_TOKEN) {
4429              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
4430              unless (length $token->{data}) {              unless (length $token->{data}) {
4431                !!!next-token;                !!!next-token;
# Line 4370  sub _tree_construction_main ($) { Line 4446  sub _tree_construction_main ($) {
4446            INSCOPE: for (reverse @{$self->{open_elements}}) {            INSCOPE: for (reverse @{$self->{open_elements}}) {
4447              if ($_->[1] eq 'p') {              if ($_->[1] eq 'p') {
4448                !!!back-token;                !!!back-token;
4449                $token = {type => 'end tag', tag_name => 'p'};                $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4450                redo B;                redo B;
4451              } elsif ({              } elsif ({
4452                        table => 1, caption => 1, td => 1, th => 1,                        table => 1, caption => 1, td => 1, th => 1,
# Line 4390  sub _tree_construction_main ($) { Line 4466  sub _tree_construction_main ($) {
4466          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4467            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4468              !!!back-token;              !!!back-token;
4469              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4470              redo B;              redo B;
4471            } elsif ({            } elsif ({
4472                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4437  sub _tree_construction_main ($) { Line 4513  sub _tree_construction_main ($) {
4513          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4514            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4515              !!!back-token;              !!!back-token;
4516              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4517              redo B;              redo B;
4518            } elsif ({            } elsif ({
4519                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4484  sub _tree_construction_main ($) { Line 4560  sub _tree_construction_main ($) {
4560          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4561            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4562              !!!back-token;              !!!back-token;
4563              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4564              redo B;              redo B;
4565            } elsif ({            } elsif ({
4566                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4508  sub _tree_construction_main ($) { Line 4584  sub _tree_construction_main ($) {
4584            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4585            if ($node->[1] eq 'p') {            if ($node->[1] eq 'p') {
4586              !!!back-token;              !!!back-token;
4587              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4588              redo B;              redo B;
4589            } elsif ({            } elsif ({
4590                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4552  sub _tree_construction_main ($) { Line 4628  sub _tree_construction_main ($) {
4628              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a');
4629                            
4630              !!!back-token;              !!!back-token;
4631              $token = {type => 'end tag', tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a'};
4632              $formatting_end_tag->($token->{tag_name});              $formatting_end_tag->($token->{tag_name});
4633                            
4634              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
# Line 4599  sub _tree_construction_main ($) { Line 4675  sub _tree_construction_main ($) {
4675          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4676            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4677            if ($node->[1] eq 'nobr') {            if ($node->[1] eq 'nobr') {
4678              !!!parse-error (type => 'not closed:nobr');              !!!parse-error (type => 'in nobr:nobr');
4679              !!!back-token;              !!!back-token;
4680              $token = {type => 'end tag', tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};
4681              redo B;              redo B;
4682            } elsif ({            } elsif ({
4683                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4623  sub _tree_construction_main ($) { Line 4699  sub _tree_construction_main ($) {
4699            if ($node->[1] eq 'button') {            if ($node->[1] eq 'button') {
4700              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button');
4701              !!!back-token;              !!!back-token;
4702              $token = {type => 'end tag', tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button'};
4703              redo B;              redo B;
4704            } elsif ({            } elsif ({
4705                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4658  sub _tree_construction_main ($) { Line 4734  sub _tree_construction_main ($) {
4734          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4735            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4736              !!!back-token;              !!!back-token;
4737              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4738              redo B;              redo B;
4739            } elsif ({            } elsif ({
4740                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4670  sub _tree_construction_main ($) { Line 4746  sub _tree_construction_main ($) {
4746                        
4747          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4748                        
4749          $self->{insertion_mode} = 'in table';          $self->{insertion_mode} = IN_TABLE_IM;
4750                        
4751          !!!next-token;          !!!next-token;
4752          redo B;          redo B;
# Line 4697  sub _tree_construction_main ($) { Line 4773  sub _tree_construction_main ($) {
4773          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4774            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
4775              !!!back-token;              !!!back-token;
4776              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p'};
4777              redo B;              redo B;
4778            } elsif ({            } elsif ({
4779                      table => 1, caption => 1, td => 1, th => 1,                      table => 1, caption => 1, td => 1, th => 1,
# Line 4737  sub _tree_construction_main ($) { Line 4813  sub _tree_construction_main ($) {
4813            delete $at->{action};            delete $at->{action};
4814            delete $at->{prompt};            delete $at->{prompt};
4815            my @tokens = (            my @tokens = (
4816                          {type => 'start tag', tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
4817                           attributes => $form_attrs},                           attributes => $form_attrs},
4818                          {type => 'start tag', tag_name => 'hr'},                          {type => START_TAG_TOKEN, tag_name => 'hr'},
4819                          {type => 'start tag', tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'p'},
4820                          {type => 'start tag', tag_name => 'label'},                          {type => START_TAG_TOKEN, tag_name => 'label'},
4821                         );                         );
4822            if ($prompt_attr) {            if ($prompt_attr) {
4823              push @tokens, {type => 'character', data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};
4824            } else {            } else {
4825              push @tokens, {type => 'character',              push @tokens, {type => CHARACTER_TOKEN,
4826                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD
4827              ## TODO: make this configurable              ## TODO: make this configurable
4828            }            }
4829            push @tokens,            push @tokens,
4830                          {type => 'start tag', tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},
4831                          #{type => 'character', data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4832                          {type => 'end tag', tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label'},
4833                          {type => 'end tag', tag_name => 'p'},                          {type => END_TAG_TOKEN, tag_name => 'p'},
4834                          {type => 'start tag', tag_name => 'hr'},                          {type => START_TAG_TOKEN, tag_name => 'hr'},
4835                          {type => 'end tag', tag_name => 'form'};                          {type => END_TAG_TOKEN, tag_name => 'form'};
4836            $token = shift @tokens;            $token = shift @tokens;
4837            !!!back-token (@tokens);            !!!back-token (@tokens);
4838            redo B;            redo B;
# Line 4774  sub _tree_construction_main ($) { Line 4850  sub _tree_construction_main ($) {
4850                    
4851          my $text = '';          my $text = '';
4852          !!!next-token;          !!!next-token;
4853          if ($token->{type} eq 'character') {          if ($token->{type} == CHARACTER_TOKEN) {
4854            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
4855            unless (length $token->{data}) {            unless (length $token->{data}) {
4856              !!!next-token;              !!!next-token;
4857            }            }
4858          }          }
4859          while ($token->{type} eq 'character') {          while ($token->{type} == CHARACTER_TOKEN) {
4860            $text .= $token->{data};            $text .= $token->{data};
4861            !!!next-token;            !!!next-token;
4862          }          }
# Line 4790  sub _tree_construction_main ($) { Line 4866  sub _tree_construction_main ($) {
4866                    
4867          $self->{content_model} = PCDATA_CONTENT_MODEL;          $self->{content_model} = PCDATA_CONTENT_MODEL;
4868                    
4869          if ($token->{type} eq 'end tag' and          if ($token->{type} == END_TAG_TOKEN and
4870              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
4871            ## Ignore the token            ## Ignore the token
4872          } else {          } else {
# Line 4804  sub _tree_construction_main ($) { Line 4880  sub _tree_construction_main ($) {
4880                  noframes => 1,                  noframes => 1,
4881                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
4882                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4883          ## NOTE: There are two "as if in body" code clones.          ## NOTE: There is an "as if in body" code clone.
4884          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4885          redo B;          redo B;
4886        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'select') {
# Line 4812  sub _tree_construction_main ($) { Line 4888  sub _tree_construction_main ($) {
4888                    
4889          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4890                    
4891          $self->{insertion_mode} = 'in select';          $self->{insertion_mode} = IN_SELECT_IM;
4892          !!!next-token;          !!!next-token;
4893          redo B;          redo B;
4894        } elsif ({        } elsif ({
# Line 4835  sub _tree_construction_main ($) { Line 4911  sub _tree_construction_main ($) {
4911          !!!next-token;          !!!next-token;
4912          redo B;          redo B;
4913        }        }
4914      } elsif ($token->{type} eq 'end tag') {      } elsif ($token->{type} == END_TAG_TOKEN) {
4915        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
4916          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
4917              $self->{open_elements}->[1]->[1] eq 'body') {              $self->{open_elements}->[1]->[1] eq 'body') {
# Line 4849  sub _tree_construction_main ($) { Line 4925  sub _tree_construction_main ($) {
4925              }              }
4926            }            }
4927    
4928            $self->{insertion_mode} = 'after body';            $self->{insertion_mode} = AFTER_BODY_IM;
4929            !!!next-token;            !!!next-token;
4930            redo B;            redo B;
4931          } else {          } else {
# Line 4864  sub _tree_construction_main ($) { Line 4940  sub _tree_construction_main ($) {
4940            if ($self->{open_elements}->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
4941              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);
4942            }            }
4943            $self->{insertion_mode} = 'after body';            $self->{insertion_mode} = AFTER_BODY_IM;
4944            ## reprocess            ## reprocess
4945            redo B;            redo B;
4946          } else {          } else {
# Line 4896  sub _tree_construction_main ($) { Line 4972  sub _tree_construction_main ($) {
4972                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
4973                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
4974                !!!back-token;                !!!back-token;
4975                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
4976                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4977                redo B;                redo B;
4978              }              }
# Line 4944  sub _tree_construction_main ($) { Line 5020  sub _tree_construction_main ($) {
5020                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
5021                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5022                !!!back-token;                !!!back-token;
5023                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5024                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5025                redo B;                redo B;
5026              }              }
# Line 4960  sub _tree_construction_main ($) { Line 5036  sub _tree_construction_main ($) {
5036          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {
5037            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
5038          } else {          } else {
5039            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5040          }          }
5041    
5042          undef $self->{form_element};          undef $self->{form_element};
# Line 4983  sub _tree_construction_main ($) { Line 5059  sub _tree_construction_main ($) {
5059                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot=> 1, thead => 1,
5060                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5061                !!!back-token;                !!!back-token;
5062                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5063                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5064                redo B;                redo B;
5065              }              }
# Line 4998  sub _tree_construction_main ($) { Line 5074  sub _tree_construction_main ($) {
5074          } # INSCOPE          } # INSCOPE
5075                    
5076          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
5077            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5078          }          }
5079                    
5080          splice @{$self->{open_elements}}, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
# Line 5057  sub _tree_construction_main ($) { Line 5133  sub _tree_construction_main ($) {
5133              if ({              if ({
5134                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
5135                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
5136                   tbody => 1, tfoot=> 1, thead => 1,                   tbody => 1, tfoot => 1, thead => 1,
5137                  }->{$self->{open_elements}->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
5138                !!!back-token;                !!!back-token;
5139                $token = {type => 'end tag',                $token = {type => END_TAG_TOKEN,
5140                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5141                redo B;                redo B;
5142              }              }
5143                    
5144              ## Step 2              ## Step 2
5145              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
5146                  ## NOTE: <x><y></x>
5147                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
5148              }              }
5149                            

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24