/[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.2 by wakaba, Tue May 1 10:47:37 2007 UTC revision 1.6 by wakaba, Sat May 26 08:12:34 2007 UTC
# Line 278  my $entity_char = { Line 278  my $entity_char = {
278    zeta => "\x{03B6}",    zeta => "\x{03B6}",
279    zwj => "\x{200D}",    zwj => "\x{200D}",
280    zwnj => "\x{200C}",    zwnj => "\x{200C}",
281  };  }; # $entity_char
282    
283    ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>
284    my $c1_entity_char = {
285         128, 8364,
286         129, 65533,
287         130, 8218,
288         131, 402,
289         132, 8222,
290         133, 8230,
291         134, 8224,
292         135, 8225,
293         136, 710,
294         137, 8240,
295         138, 352,
296         139, 8249,
297         140, 338,
298         141, 65533,
299         142, 381,
300         143, 65533,
301         144, 65533,
302         145, 8216,
303         146, 8217,
304         147, 8220,
305         148, 8221,
306         149, 8226,
307         150, 8211,
308         151, 8212,
309         152, 732,
310         153, 8482,
311         154, 353,
312         155, 8250,
313         156, 339,
314         157, 65533,
315         158, 382,
316         159, 376,
317    }; # $c1_entity_char
318    
319  my $special_category = {  my $special_category = {
320    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,
# Line 307  sub parse_string ($$$;$) { Line 343  sub parse_string ($$$;$) {
343    my $s = \$_[0];    my $s = \$_[0];
344    $self->{document} = $_[1];    $self->{document} = $_[1];
345    
346    my $i;    ## NOTE: |set_inner_html| copies most of this method's code
347    
348    my $i = 0;    my $i = 0;
349      my $line = 1;
350      my $column = 0;
351    $self->{set_next_input_character} = sub {    $self->{set_next_input_character} = sub {
352      my $self = shift;      my $self = shift;
353      $self->{next_input_character} = -1 and return if $i >= length $$s;      $self->{next_input_character} = -1 and return if $i >= length $$s;
354      $self->{next_input_character} = ord substr $$s, $i++, 1;      $self->{next_input_character} = ord substr $$s, $i++, 1;
355        $column++;
356            
357      if ($self->{next_input_character} == 0x000D) { # CR      if ($self->{next_input_character} == 0x000A) { # LF
358          $line++;
359          $column = 0;
360        } elsif ($self->{next_input_character} == 0x000D) { # CR
361        if ($i >= length $$s) {        if ($i >= length $$s) {
362          #          #
363        } else {        } else {
# Line 326  sub parse_string ($$$;$) { Line 369  sub parse_string ($$$;$) {
369          }          }
370        }        }
371        $self->{next_input_character} = 0x000A; # LF # MUST        $self->{next_input_character} = 0x000A; # LF # MUST
372          $line++;
373          $column = 0;
374      } elsif ($self->{next_input_character} > 0x10FFFF) {      } elsif ($self->{next_input_character} > 0x10FFFF) {
375        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
376      } elsif ($self->{next_input_character} == 0x0000) { # NULL      } elsif ($self->{next_input_character} == 0x0000) { # NULL
# Line 333  sub parse_string ($$$;$) { Line 378  sub parse_string ($$$;$) {
378      }      }
379    };    };
380    
381    $self->{parse_error} = $_[2] || sub {    my $onerror = $_[2] || sub {
382      warn "Parse error at character $i\n"; ## TODO: Report (line, column) pair      my (%opt) = @_;
383        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";
384      };
385      $self->{parse_error} = sub {
386        $onerror->(@_, line => $line, column => $column);
387    };    };
388    
389    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 488  sub _get_next_token ($) { Line 537  sub _get_next_token ($) {
537            !!!next-input-character;            !!!next-input-character;
538            redo A;            redo A;
539          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_input_character} == 0x003E) { # >
540            !!!parse-error;            !!!parse-error (type => 'empty start tag');
541            $self->{state} = 'data';            $self->{state} = 'data';
542            !!!next-input-character;            !!!next-input-character;
543    
# Line 496  sub _get_next_token ($) { Line 545  sub _get_next_token ($) {
545    
546            redo A;            redo A;
547          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_input_character} == 0x003F) { # ?
548            !!!parse-error;            !!!parse-error (type => 'pio');
549            $self->{state} = 'bogus comment';            $self->{state} = 'bogus comment';
550            ## $self->{next_input_character} is intentionally left as is            ## $self->{next_input_character} is intentionally left as is
551            redo A;            redo A;
552          } else {          } else {
553            !!!parse-error;            !!!parse-error (type => 'bare stago');
554            $self->{state} = 'data';            $self->{state} = 'data';
555            ## reconsume            ## reconsume
556    
# Line 524  sub _get_next_token ($) { Line 573  sub _get_next_token ($) {
573              !!!next-input-character;              !!!next-input-character;
574              next TAGNAME;              next TAGNAME;
575            } else {            } else {
576              !!!parse-error;              !!!parse-error (type => 'unmatched end tag');
577              $self->{next_input_character} = shift @next_char; # reconsume              $self->{next_input_character} = shift @next_char; # reconsume
578              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
579              $self->{state} = 'data';              $self->{state} = 'data';
# Line 545  sub _get_next_token ($) { Line 594  sub _get_next_token ($) {
594                  $self->{next_input_character} == 0x002F or # /                  $self->{next_input_character} == 0x002F or # /
595                  $self->{next_input_character} == 0x003C or # <                  $self->{next_input_character} == 0x003C or # <
596                  $self->{next_input_character} == -1) {                  $self->{next_input_character} == -1) {
597            !!!parse-error;            !!!parse-error (type => 'unmatched end tag');
598            $self->{next_input_character} = shift @next_char; # reconsume            $self->{next_input_character} = shift @next_char; # reconsume
599            !!!back-next-input-character (@next_char);            !!!back-next-input-character (@next_char);
600            $self->{state} = 'data';            $self->{state} = 'data';
# Line 575  sub _get_next_token ($) { Line 624  sub _get_next_token ($) {
624          !!!next-input-character;          !!!next-input-character;
625          redo A;          redo A;
626        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
627          !!!parse-error;          !!!parse-error (type => 'empty end tag');
628          $self->{state} = 'data';          $self->{state} = 'data';
629          !!!next-input-character;          !!!next-input-character;
630          redo A;          redo A;
631        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
632          !!!parse-error;          !!!parse-error (type => 'bare etago');
633          $self->{state} = 'data';          $self->{state} = 'data';
634          # reconsume          # reconsume
635    
# Line 588  sub _get_next_token ($) { Line 637  sub _get_next_token ($) {
637    
638          redo A;          redo A;
639        } else {        } else {
640          !!!parse-error;          !!!parse-error (type => 'bogus end tag');
641          $self->{state} = 'bogus comment';          $self->{state} = 'bogus comment';
642          ## $self->{next_input_character} is intentionally left as is          ## $self->{next_input_character} is intentionally left as is
643          redo A;          redo A;
# Line 608  sub _get_next_token ($) { Line 657  sub _get_next_token ($) {
657          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
658            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
659            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
660              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
661            }            }
662          } else {          } else {
663            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 629  sub _get_next_token ($) { Line 678  sub _get_next_token ($) {
678          redo A;          redo A;
679        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
680                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
681          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
682          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
683            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
684          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
685            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
686            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
687              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
688            }            }
689          } else {          } else {
690            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 655  sub _get_next_token ($) { Line 704  sub _get_next_token ($) {
704            # permitted slash            # permitted slash
705            #            #
706          } else {          } else {
707            !!!parse-error;            !!!parse-error (type => 'nestc');
708          }          }
709          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
710          # next-input-character is already done          # next-input-character is already done
# Line 682  sub _get_next_token ($) { Line 731  sub _get_next_token ($) {
731          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
732            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
733            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
734              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
735            }            }
736          } else {          } else {
737            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 709  sub _get_next_token ($) { Line 758  sub _get_next_token ($) {
758            # permitted slash            # permitted slash
759            #            #
760          } else {          } else {
761            !!!parse-error;            !!!parse-error (type => 'nestc');
762          }          }
763          ## Stay in the state          ## Stay in the state
764          # next-input-character is already done          # next-input-character is already done
765          redo A;          redo A;
766        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
767                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
768          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
769          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
770            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
771          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
772            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
773            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
774              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
775            }            }
776          } else {          } else {
777            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 745  sub _get_next_token ($) { Line 794  sub _get_next_token ($) {
794        my $before_leave = sub {        my $before_leave = sub {
795          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
796              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
797            !!!parse-error;            !!!parse-error (type => 'dupulicate attribute');
798            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
799          } else {          } else {
800            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
# Line 774  sub _get_next_token ($) { Line 823  sub _get_next_token ($) {
823          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
824            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
825            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
826              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
827            }            }
828          } else {          } else {
829            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 801  sub _get_next_token ($) { Line 850  sub _get_next_token ($) {
850            # permitted slash            # permitted slash
851            #            #
852          } else {          } else {
853            !!!parse-error;            !!!parse-error (type => 'nestc');
854          }          }
855          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
856          # next-input-character is already done          # next-input-character is already done
857          redo A;          redo A;
858        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
859                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
860          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
861          $before_leave->();          $before_leave->();
862          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
863            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
864          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
865            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
866            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
867              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
868            }            }
869          } else {          } else {
870            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 852  sub _get_next_token ($) { Line 901  sub _get_next_token ($) {
901          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
902            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
903            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
904              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
905            }            }
906          } else {          } else {
907            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 879  sub _get_next_token ($) { Line 928  sub _get_next_token ($) {
928            # permitted slash            # permitted slash
929            #            #
930          } else {          } else {
931            !!!parse-error;            !!!parse-error (type => 'nestc');
932          }          }
933          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
934          # next-input-character is already done          # next-input-character is already done
935          redo A;          redo A;
936        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
937                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
938          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
939          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
940            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
941          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
942            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
943            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
944              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
945            }            }
946          } else {          } else {
947            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 938  sub _get_next_token ($) { Line 987  sub _get_next_token ($) {
987          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
988            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
989            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
990              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
991            }            }
992          } else {          } else {
993            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 952  sub _get_next_token ($) { Line 1001  sub _get_next_token ($) {
1001          redo A;          redo A;
1002        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
1003                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
1004          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
1005          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1006            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1007          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1008            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1009            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1010              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1011            }            }
1012          } else {          } else {
1013            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 987  sub _get_next_token ($) { Line 1036  sub _get_next_token ($) {
1036          !!!next-input-character;          !!!next-input-character;
1037          redo A;          redo A;
1038        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1039          !!!parse-error;          !!!parse-error (type => 'unclosed attribute value');
1040          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1041            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1042          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1043            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1044            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1045              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1046            }            }
1047          } else {          } else {
1048            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1022  sub _get_next_token ($) { Line 1071  sub _get_next_token ($) {
1071          !!!next-input-character;          !!!next-input-character;
1072          redo A;          redo A;
1073        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1074          !!!parse-error;          !!!parse-error (type => 'unclosed attribute value');
1075          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1076            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1077          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1078            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1079            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1080              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1081            }            }
1082          } else {          } else {
1083            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1066  sub _get_next_token ($) { Line 1115  sub _get_next_token ($) {
1115          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1116            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1117            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1118              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1119            }            }
1120          } else {          } else {
1121            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1080  sub _get_next_token ($) { Line 1129  sub _get_next_token ($) {
1129          redo A;          redo A;
1130        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
1131                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
1132          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
1133          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1134            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1135          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1136            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1137            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1138              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1139            }            }
1140          } else {          } else {
1141            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
# Line 1196  sub _get_next_token ($) { Line 1245  sub _get_next_token ($) {
1245          }          }
1246        }        }
1247    
1248        !!!parse-error;        !!!parse-error (type => 'bogus comment open');
1249        $self->{next_input_character} = shift @next_char;        $self->{next_input_character} = shift @next_char;
1250        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1251        $self->{state} = 'bogus comment';        $self->{state} = 'bogus comment';
# Line 1210  sub _get_next_token ($) { Line 1259  sub _get_next_token ($) {
1259          !!!next-input-character;          !!!next-input-character;
1260          redo A;          redo A;
1261        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1262          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1263          $self->{state} = 'data';          $self->{state} = 'data';
1264          ## reconsume          ## reconsume
1265    
# Line 1230  sub _get_next_token ($) { Line 1279  sub _get_next_token ($) {
1279          !!!next-input-character;          !!!next-input-character;
1280          redo A;          redo A;
1281        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1282          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1283          $self->{state} = 'data';          $self->{state} = 'data';
1284          ## reconsume          ## reconsume
1285    
# Line 1254  sub _get_next_token ($) { Line 1303  sub _get_next_token ($) {
1303    
1304          redo A;          redo A;
1305        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_input_character} == 0x002D) { # -
1306          !!!parse-error;          !!!parse-error (type => 'dash in comment');
1307          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
1308          ## Stay in the state          ## Stay in the state
1309          !!!next-input-character;          !!!next-input-character;
1310          redo A;          redo A;
1311        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1312          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1313          $self->{state} = 'data';          $self->{state} = 'data';
1314          ## reconsume          ## reconsume
1315    
# Line 1269  sub _get_next_token ($) { Line 1318  sub _get_next_token ($) {
1318    
1319          redo A;          redo A;
1320        } else {        } else {
1321          !!!parse-error;          !!!parse-error (type => 'dash in comment');
1322          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment
1323          $self->{state} = 'comment';          $self->{state} = 'comment';
1324          !!!next-input-character;          !!!next-input-character;
# Line 1285  sub _get_next_token ($) { Line 1334  sub _get_next_token ($) {
1334          !!!next-input-character;          !!!next-input-character;
1335          redo A;          redo A;
1336        } else {        } else {
1337          !!!parse-error;          !!!parse-error (type => 'no space before DOCTYPE name');
1338          $self->{state} = 'before DOCTYPE name';          $self->{state} = 'before DOCTYPE name';
1339          ## reconsume          ## reconsume
1340          redo A;          redo A;
# Line 1301  sub _get_next_token ($) { Line 1350  sub _get_next_token ($) {
1350          redo A;          redo A;
1351        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{next_input_character} and
1352                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{next_input_character} <= 0x007A) { # a..z
1353    ## ISSUE: "Set the token's name name to the" in the spec
1354          $self->{current_token} = {type => 'DOCTYPE',          $self->{current_token} = {type => 'DOCTYPE',
1355                            name => chr ($self->{next_input_character} - 0x0020),                            name => chr ($self->{next_input_character} - 0x0020),
1356                            error => 1};                            error => 1};
# Line 1308  sub _get_next_token ($) { Line 1358  sub _get_next_token ($) {
1358          !!!next-input-character;          !!!next-input-character;
1359          redo A;          redo A;
1360        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1361          !!!parse-error;          !!!parse-error (type => 'no DOCTYPE name');
1362          $self->{state} = 'data';          $self->{state} = 'data';
1363          !!!next-input-character;          !!!next-input-character;
1364    
# Line 1316  sub _get_next_token ($) { Line 1366  sub _get_next_token ($) {
1366    
1367          redo A;          redo A;
1368        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1369          !!!parse-error;          !!!parse-error (type => 'no DOCTYPE name');
1370          $self->{state} = 'data';          $self->{state} = 'data';
1371          ## reconsume          ## reconsume
1372    
# Line 1327  sub _get_next_token ($) { Line 1377  sub _get_next_token ($) {
1377          $self->{current_token} = {type => 'DOCTYPE',          $self->{current_token} = {type => 'DOCTYPE',
1378                            name => chr ($self->{next_input_character}),                            name => chr ($self->{next_input_character}),
1379                            error => 1};                            error => 1};
1380    ## ISSUE: "Set the token's name name to the" in the spec
1381          $self->{state} = 'DOCTYPE name';          $self->{state} = 'DOCTYPE name';
1382          !!!next-input-character;          !!!next-input-character;
1383          redo A;          redo A;
# Line 1358  sub _get_next_token ($) { Line 1409  sub _get_next_token ($) {
1409          !!!next-input-character;          !!!next-input-character;
1410          redo A;          redo A;
1411        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1412          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1413          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE
1414          $self->{state} = 'data';          $self->{state} = 'data';
1415          ## reconsume          ## reconsume
# Line 1393  sub _get_next_token ($) { Line 1444  sub _get_next_token ($) {
1444    
1445          redo A;          redo A;
1446        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1447          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1448          $self->{state} = 'data';          $self->{state} = 'data';
1449          ## reconsume          ## reconsume
1450    
# Line 1402  sub _get_next_token ($) { Line 1453  sub _get_next_token ($) {
1453    
1454          redo A;          redo A;
1455        } else {        } else {
1456          !!!parse-error;          !!!parse-error (type => 'string after DOCTYPE name');
1457          $self->{current_token}->{error} = 1; # DOCTYPE          $self->{current_token}->{error} = 1; # DOCTYPE
1458          $self->{state} = 'bogus DOCTYPE';          $self->{state} = 'bogus DOCTYPE';
1459          !!!next-input-character;          !!!next-input-character;
# Line 1418  sub _get_next_token ($) { Line 1469  sub _get_next_token ($) {
1469    
1470          redo A;          redo A;
1471        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1472          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1473          $self->{state} = 'data';          $self->{state} = 'data';
1474          ## reconsume          ## reconsume
1475    
# Line 1444  sub _tokenize_attempt_to_consume_an_enti Line 1495  sub _tokenize_attempt_to_consume_an_enti
1495        
1496    if ($self->{next_input_character} == 0x0023) { # #    if ($self->{next_input_character} == 0x0023) { # #
1497      !!!next-input-character;      !!!next-input-character;
     my $num;  
1498      if ($self->{next_input_character} == 0x0078 or # x      if ($self->{next_input_character} == 0x0078 or # x
1499          $self->{next_input_character} == 0x0058) { # X          $self->{next_input_character} == 0x0058) { # X
1500          my $num;
1501        X: {        X: {
1502          my $x_char = $self->{next_input_character};          my $x_char = $self->{next_input_character};
1503          !!!next-input-character;          !!!next-input-character;
# Line 1471  sub _tokenize_attempt_to_consume_an_enti Line 1522  sub _tokenize_attempt_to_consume_an_enti
1522            $num += $self->{next_input_character} - 0x0040 + 9;            $num += $self->{next_input_character} - 0x0040 + 9;
1523            redo X;            redo X;
1524          } elsif (not defined $num) { # no hexadecimal digit          } elsif (not defined $num) { # no hexadecimal digit
1525            !!!parse-error;            !!!parse-error (type => 'bare hcro');
1526            $self->{next_input_character} = 0x0023; # #            $self->{next_input_character} = 0x0023; # #
1527            !!!back-next-input-character ($x_char);            !!!back-next-input-character ($x_char);
1528            return undef;            return undef;
1529          } elsif ($self->{next_input_character} == 0x003B) { # ;          } elsif ($self->{next_input_character} == 0x003B) { # ;
1530            !!!next-input-character;            !!!next-input-character;
1531          } else {          } else {
1532            !!!parse-error;            !!!parse-error (type => 'no refc');
1533          }          }
1534    
1535          ## TODO: check the definition for |a valid Unicode character|.          ## TODO: check the definition for |a valid Unicode character|.
1536            ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8189>
1537          if ($num > 1114111 or $num == 0) {          if ($num > 1114111 or $num == 0) {
1538            $num = 0xFFFD; # REPLACEMENT CHARACTER            $num = 0xFFFD; # REPLACEMENT CHARACTER
1539            ## ISSUE: Why this is not an error?            ## ISSUE: Why this is not an error?
1540            } elsif (0x80 <= $num and $num <= 0x9F) {
1541              ## NOTE: <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>
1542              ## ISSUE: Not in the spec yet; parse error?
1543              $num = $c1_entity_char->{$num};
1544          }          }
1545    
1546          return {type => 'character', data => chr $num};          return {type => 'character', data => chr $num};
# Line 1505  sub _tokenize_attempt_to_consume_an_enti Line 1561  sub _tokenize_attempt_to_consume_an_enti
1561        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_input_character} == 0x003B) { # ;
1562          !!!next-input-character;          !!!next-input-character;
1563        } else {        } else {
1564          !!!parse-error;          !!!parse-error (type => 'no refc');
1565        }        }
1566    
1567        ## TODO: check the definition for |a valid Unicode character|.        ## TODO: check the definition for |a valid Unicode character|.
1568        if ($code > 1114111 or $code == 0) {        if ($code > 1114111 or $code == 0) {
1569          $code = 0xFFFD; # REPLACEMENT CHARACTER          $code = 0xFFFD; # REPLACEMENT CHARACTER
1570          ## ISSUE: Why this is not an error?          ## ISSUE: Why this is not an error?
1571          } elsif (0x80 <= $code and $code <= 0x9F) {
1572            ## NOTE: <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>
1573            ## ISSUE: Not in the spec yet; parse error?
1574            $code = $c1_entity_char->{$code};
1575        }        }
1576                
1577        return {type => 'character', data => chr $code};        return {type => 'character', data => chr $code};
1578      } else {      } else {
1579        !!!parse-error;        !!!parse-error (type => 'bare nero');
1580        !!!back-next-input-character ($self->{next_input_character});        !!!back-next-input-character ($self->{next_input_character});
1581        $self->{next_input_character} = 0x0023; # #        $self->{next_input_character} = 0x0023; # #
1582        return undef;        return undef;
# Line 1553  sub _tokenize_attempt_to_consume_an_enti Line 1613  sub _tokenize_attempt_to_consume_an_enti
1613        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_input_character} == 0x003B) { # ;
1614          !!!next-input-character;          !!!next-input-character;
1615        } else {        } else {
1616          !!!parse-error;          !!!parse-error (type => 'refc');
1617        }        }
1618    
1619        return {type => 'character', data => $value};        return {type => 'character', data => $value};
1620      } else {      } else {
1621        !!!parse-error;        !!!parse-error (type => 'bare ero');
1622        ## NOTE: No characters are consumed in the spec.        ## NOTE: No characters are consumed in the spec.
1623        !!!back-token ({type => 'character', data => $value});        !!!back-token ({type => 'character', data => $value});
1624        return undef;        return undef;
1625      }      }
1626    } else {    } else {
1627      ## no characters are consumed      ## no characters are consumed
1628      !!!parse-error;      !!!parse-error (type => 'bare ero');
1629      return undef;      return undef;
1630    }    }
1631  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 1587  sub _terminate_tree_constructor ($) { Line 1647  sub _terminate_tree_constructor ($) {
1647    
1648  ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?  ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
1649    
1650    { # tree construction stage
1651      my $token;
1652    
1653  sub _construct_tree ($) {  sub _construct_tree ($) {
1654    my ($self) = @_;    my ($self) = @_;
1655    
# Line 1598  sub _construct_tree ($) { Line 1661  sub _construct_tree ($) {
1661    ## characters and insert one Text node whose data is concatenation    ## characters and insert one Text node whose data is concatenation
1662    ## of all those characters. # MUST    ## of all those characters. # MUST
1663        
   my $token;  
1664    !!!next-token;    !!!next-token;
1665    
1666    my $phase = 'initial'; # MUST    $self->{insertion_mode} = 'before head';
1667      undef $self->{form_element};
1668      undef $self->{head_element};
1669      $self->{open_elements} = [];
1670      undef $self->{inner_html_node};
1671    
1672      $self->_tree_construction_initial; # MUST
1673      $self->_tree_construction_root_element;
1674      $self->_tree_construction_main;
1675    } # _construct_tree
1676    
1677    sub _tree_construction_initial ($) {
1678      my $self = shift;
1679      B: {
1680          if ($token->{type} eq 'DOCTYPE') {
1681            if ($token->{error}) {
1682              ## ISSUE: Spec currently left this case undefined.
1683              !!!parse-error (type => 'bogus DOCTYPE');
1684            }
1685            my $doctype = $self->{document}->create_document_type_definition
1686              ($token->{name});
1687            $self->{document}->append_child ($doctype);
1688            #$phase = 'root element';
1689            !!!next-token;
1690            #redo B;
1691            return;
1692          } elsif ({
1693                    comment => 1,
1694                    'start tag' => 1,
1695                    'end tag' => 1,
1696                    'end-of-file' => 1,
1697                   }->{$token->{type}}) {
1698            ## ISSUE: Spec currently left this case undefined.
1699            !!!parse-error (type => 'missing DOCTYPE');
1700            #$phase = 'root element';
1701            ## reprocess
1702            #redo B;
1703            return;
1704          } elsif ($token->{type} eq 'character') {
1705            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
1706              $self->{document}->manakai_append_text ($1);
1707              ## ISSUE: DOM3 Core does not allow Document > Text
1708              unless (length $token->{data}) {
1709                ## Stay in the phase
1710                !!!next-token;
1711                redo B;
1712              }
1713            }
1714            ## ISSUE: Spec currently left this case undefined.
1715            !!!parse-error (type => 'missing DOCTYPE');
1716            #$phase = 'root element';
1717            ## reprocess
1718            #redo B;
1719            return;
1720          } else {
1721            die "$0: $token->{type}: Unknown token";
1722          }
1723        } # B
1724    } # _tree_construction_initial
1725    
1726    sub _tree_construction_root_element ($) {
1727      my $self = shift;
1728      
1729      B: {
1730          if ($token->{type} eq 'DOCTYPE') {
1731            !!!parse-error (type => 'in html:#DOCTYPE');
1732            ## Ignore the token
1733            ## Stay in the phase
1734            !!!next-token;
1735            redo B;
1736          } elsif ($token->{type} eq 'comment') {
1737            my $comment = $self->{document}->create_comment ($token->{data});
1738            $self->{document}->append_child ($comment);
1739            ## Stay in the phase
1740            !!!next-token;
1741            redo B;
1742          } elsif ($token->{type} eq 'character') {
1743            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
1744              $self->{document}->manakai_append_text ($1);
1745              ## ISSUE: DOM3 Core does not allow Document > Text
1746              unless (length $token->{data}) {
1747                ## Stay in the phase
1748                !!!next-token;
1749                redo B;
1750              }
1751            }
1752            #
1753          } elsif ({
1754                    'start tag' => 1,
1755                    'end tag' => 1,
1756                    'end-of-file' => 1,
1757                   }->{$token->{type}}) {
1758            ## ISSUE: There is an issue in the spec
1759            #
1760          } else {
1761            die "$0: $token->{type}: Unknown token";
1762          }
1763          my $root_element; !!!create-element ($root_element, 'html');
1764          $self->{document}->append_child ($root_element);
1765          push @{$self->{open_elements}}, [$root_element, 'html'];
1766          #$phase = 'main';
1767          ## reprocess
1768          #redo B;
1769          return;
1770      } # B
1771    } # _tree_construction_root_element
1772    
1773    sub _reset_insertion_mode ($) {
1774      my $self = shift;
1775    
1776        ## Step 1
1777        my $last;
1778        
1779        ## Step 2
1780        my $i = -1;
1781        my $node = $self->{open_elements}->[$i];
1782        
1783        ## Step 3
1784        S3: {
1785          $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];
1786          if (defined $self->{inner_html_node}) {
1787            if ($self->{inner_html_node}->[1] eq 'td' or
1788                $self->{inner_html_node}->[1] eq 'th') {
1789              #
1790            } else {
1791              $node = $self->{inner_html_node};
1792            }
1793          }
1794        
1795          ## Step 4..13
1796          my $new_mode = {
1797                          select => 'in select',
1798                          td => 'in cell',
1799                          th => 'in cell',
1800                          tr => 'in row',
1801                          tbody => 'in table body',
1802                          thead => 'in table head',
1803                          tfoot => 'in table foot',
1804                          caption => 'in caption',
1805                          colgroup => 'in column group',
1806                          table => 'in table',
1807                          head => 'in body', # not in head!
1808                          body => 'in body',
1809                          frameset => 'in frameset',
1810                         }->{$node->[1]};
1811          $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1812          
1813          ## Step 14
1814          if ($node->[1] eq 'html') {
1815            unless (defined $self->{head_element}) {
1816              $self->{insertion_mode} = 'before head';
1817            } else {
1818              $self->{insertion_mode} = 'after head';
1819            }
1820            return;
1821          }
1822          
1823          ## Step 15
1824          $self->{insertion_mode} = 'in body' and return if $last;
1825          
1826          ## Step 16
1827          $i--;
1828          $node = $self->{open_elements}->[$i];
1829          
1830          ## Step 17
1831          redo S3;
1832        } # S3
1833    } # _reset_insertion_mode
1834    
1835    sub _tree_construction_main ($) {
1836      my $self = shift;
1837    
1838      my $phase = 'main';
1839    
   my $open_elements = [];  
1840    my $active_formatting_elements = [];    my $active_formatting_elements = [];
   my $head_element;  
   my $form_element;  
   my $insertion_mode = 'before head';  
1841    
1842    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
1843      my $insert = shift;      my $insert = shift;
# Line 1621  sub _construct_tree ($) { Line 1851  sub _construct_tree ($) {
1851    
1852      ## Step 2      ## Step 2
1853      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
1854      for (@$open_elements) {      for (@{$self->{open_elements}}) {
1855        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
1856          return;          return;
1857        }        }
# Line 1640  sub _construct_tree ($) { Line 1870  sub _construct_tree ($) {
1870          #          #
1871        } else {        } else {
1872          my $in_open_elements;          my $in_open_elements;
1873          OE: for (@$open_elements) {          OE: for (@{$self->{open_elements}}) {
1874            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
1875              $in_open_elements = 1;              $in_open_elements = 1;
1876              last OE;              last OE;
# Line 1664  sub _construct_tree ($) { Line 1894  sub _construct_tree ($) {
1894            
1895        ## Step 9        ## Step 9
1896        $insert->($clone->[0]);        $insert->($clone->[0]);
1897        push @$open_elements, $clone;        push @{$self->{open_elements}}, $clone;
1898                
1899        ## Step 10        ## Step 10
1900        $active_formatting_elements->[$i] = $open_elements->[-1];        $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
1901    
1902        ## Step 11        ## Step 11
1903        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
# Line 1689  sub _construct_tree ($) { Line 1919  sub _construct_tree ($) {
1919      }      }
1920    }; # $clear_up_to_marker    }; # $clear_up_to_marker
1921    
   my $reset_insertion_mode = sub {  
     ## Step 1  
     my $last;  
       
     ## Step 2  
     my $i = -1;  
     my $node = $open_elements->[$i];  
       
     ## Step 3  
     S3: {  
       $last = 1 if $open_elements->[0]->[0] eq $node->[0];  
       ## TODO: the element whose inner_html is set is neither td nor th, then $node = the element  
       
       ## Step 4..13  
       my $new_mode = {  
                       select => 'in select',  
                       td => 'in cell',  
                       th => 'in cell',  
                       tr => 'in row',  
                       tbody => 'in table body',  
                       thead => 'in table head',  
                       tfoot => 'in table foot',  
                       caption => 'in caption',  
                       colgroup => 'in column group',  
                       table => 'in table',  
                       head => 'in body', # not in head!  
                       body => 'in body',  
                       frameset => 'in frameset',  
                      }->{$node->[1]};  
       $insertion_mode = $new_mode and return if defined $new_mode;  
         
       ## Step 14  
       if ($node->[1] eq 'html') {  
         unless (defined $head_element) {  
           $insertion_mode = 'before head';  
         } else {  
           $insertion_mode = 'after head';  
         }  
         return;  
       }  
         
       ## Step 15  
       $insertion_mode = 'in body' and return if $last;  
         
       ## Step 16  
       $i--;  
       $node = $open_elements->[$i];  
         
       ## Step 17  
       redo S3;  
     } # S3  
   }; # $reset_insertion_mode  
   
1922    my $style_start_tag = sub {    my $style_start_tag = sub {
1923      my $style_el; !!!create-element ($style_el, 'style');      my $style_el; !!!create-element ($style_el, 'style', $token->{attributes});
1924      ## $insertion_mode eq 'in head' and ... (always true)      ## $self->{insertion_mode} eq 'in head' and ... (always true)
1925      (($insertion_mode eq 'in head' and defined $head_element)      (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})
1926       ? $head_element : $open_elements->[-1]->[0])       ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
1927        ->append_child ($style_el);        ->append_child ($style_el);
1928      $self->{content_model_flag} = 'CDATA';      $self->{content_model_flag} = 'CDATA';
1929                                
# Line 1765  sub _construct_tree ($) { Line 1942  sub _construct_tree ($) {
1942      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {
1943        ## Ignore the token        ## Ignore the token
1944      } else {      } else {
1945        !!!parse-error;        !!!parse-error (type => 'in CDATA:#'.$token->{type});
1946        ## ISSUE: And ignore?        ## ISSUE: And ignore?
1947      }      }
1948      !!!next-token;      !!!next-token;
# Line 1794  sub _construct_tree ($) { Line 1971  sub _construct_tree ($) {
1971          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
1972        ## Ignore the token        ## Ignore the token
1973      } else {      } else {
1974        !!!parse-error;        !!!parse-error (type => 'in CDATA:#'.$token->{type});
1975        ## ISSUE: And ignore?        ## ISSUE: And ignore?
1976        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
1977      }      }
1978            
1979      ## TODO: inner_html mode then mark as "already executed" and skip      if (defined $self->{inner_html_node}) {
1980      if (1) {        ## TODO: mark as "already executed"
1981        } else {
1982        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
1983        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
1984                
1985        (($insertion_mode eq 'in head' and defined $head_element)        (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})
1986         ? $head_element : $open_elements->[-1]->[0])->append_child ($script_el);         ? $self->{head_element} : $self->{open_elements}->[-1]->[0])->append_child ($script_el);
1987                
1988        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
1989                
# Line 1832  sub _construct_tree ($) { Line 2010  sub _construct_tree ($) {
2010          }          }
2011        } # AFE        } # AFE
2012        unless (defined $formatting_element) {        unless (defined $formatting_element) {
2013          !!!parse-error;          !!!parse-error (type => 'unmatched end tag:'.$tag_name);
2014          ## Ignore the token          ## Ignore the token
2015          !!!next-token;          !!!next-token;
2016          return;          return;
# Line 1840  sub _construct_tree ($) { Line 2018  sub _construct_tree ($) {
2018        ## has an element in scope        ## has an element in scope
2019        my $in_scope = 1;        my $in_scope = 1;
2020        my $formatting_element_i_in_open;          my $formatting_element_i_in_open;  
2021        INSCOPE: for (reverse 0..$#$open_elements) {        INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2022          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
2023          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
2024            if ($in_scope) {            if ($in_scope) {
2025              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
2026              last INSCOPE;              last INSCOPE;
2027            } else { # in open elements but not in scope            } else { # in open elements but not in scope
2028              !!!parse-error;              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2029              ## Ignore the token              ## Ignore the token
2030              !!!next-token;              !!!next-token;
2031              return;              return;
# Line 1860  sub _construct_tree ($) { Line 2038  sub _construct_tree ($) {
2038          }          }
2039        } # INSCOPE        } # INSCOPE
2040        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
2041          !!!parse-error;          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2042          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
2043          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
2044          return;          return;
2045        }        }
2046        if (not $open_elements->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
2047          !!!parse-error;          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2048        }        }
2049                
2050        ## Step 2        ## Step 2
2051        my $furthest_block;        my $furthest_block;
2052        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
2053        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
2054          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
2055          if (not $formatting_category->{$node->[1]} and          if (not $formatting_category->{$node->[1]} and
2056              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
2057              ($special_category->{$node->[1]} or              ($special_category->{$node->[1]} or
# Line 1887  sub _construct_tree ($) { Line 2065  sub _construct_tree ($) {
2065                
2066        ## Step 3        ## Step 3
2067        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
2068          splice @$open_elements, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
2069          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
2070          !!!next-token;          !!!next-token;
2071          return;          return;
2072        }        }
2073                
2074        ## Step 4        ## Step 4
2075        my $common_ancestor_node = $open_elements->[$formatting_element_i_in_open - 1];        my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
2076                
2077        ## Step 5        ## Step 5
2078        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
# Line 1914  sub _construct_tree ($) { Line 2092  sub _construct_tree ($) {
2092        S7: {        S7: {
2093          ## Step 1          ## Step 1
2094          $node_i_in_open--;          $node_i_in_open--;
2095          $node = $open_elements->[$node_i_in_open];          $node = $self->{open_elements}->[$node_i_in_open];
2096                    
2097          ## Step 2          ## Step 2
2098          my $node_i_in_active;          my $node_i_in_active;
# Line 1925  sub _construct_tree ($) { Line 2103  sub _construct_tree ($) {
2103                last S7S2;                last S7S2;
2104              }              }
2105            }            }
2106            splice @$open_elements, $node_i_in_open, 1;            splice @{$self->{open_elements}}, $node_i_in_open, 1;
2107            redo S7;            redo S7;
2108          } # S7S2          } # S7S2
2109                    
# Line 1941  sub _construct_tree ($) { Line 2119  sub _construct_tree ($) {
2119          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
2120            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
2121            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
2122            $open_elements->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
2123            $node = $clone;            $node = $clone;
2124          }          }
2125                    
# Line 1983  sub _construct_tree ($) { Line 2161  sub _construct_tree ($) {
2161                
2162        ## Step 13        ## Step 13
2163        undef $i;        undef $i;
2164        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
2165          if ($open_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
2166            splice @$open_elements, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
2167            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
2168          } elsif ($open_elements->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
2169            $i = $_;            $i = $_;
2170          }          }
2171        } # OE        } # OE
2172        splice @$open_elements, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 1, $clone;
2173                
2174        ## Step 14        ## Step 14
2175        redo FET;        redo FET;
# Line 1999  sub _construct_tree ($) { Line 2177  sub _construct_tree ($) {
2177    }; # $formatting_end_tag    }; # $formatting_end_tag
2178    
2179    my $insert_to_current = sub {    my $insert_to_current = sub {
2180      $open_elements->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child (shift);
2181    }; # $insert_to_current    }; # $insert_to_current
2182    
2183    my $insert_to_foster = sub {    my $insert_to_foster = sub {
# Line 2007  sub _construct_tree ($) { Line 2185  sub _construct_tree ($) {
2185                         if ({                         if ({
2186                              table => 1, tbody => 1, tfoot => 1,                              table => 1, tbody => 1, tfoot => 1,
2187                              thead => 1, tr => 1,                              thead => 1, tr => 1,
2188                             }->{$open_elements->[-1]->[1]}) {                             }->{$self->{open_elements}->[-1]->[1]}) {
2189                           # MUST                           # MUST
2190                           my $foster_parent_element;                           my $foster_parent_element;
2191                           my $next_sibling;                           my $next_sibling;
2192                           OE: for (reverse 0..$#$open_elements) {                           OE: for (reverse 0..$#{$self->{open_elements}}) {
2193                             if ($open_elements->[$_]->[1] eq 'table') {                             if ($self->{open_elements}->[$_]->[1] eq 'table') {
2194                               my $parent = $open_elements->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
2195                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
2196                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
2197                                 $next_sibling = $open_elements->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
2198                               } else {                               } else {
2199                                 $foster_parent_element                                 $foster_parent_element
2200                                   = $open_elements->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
2201                               }                               }
2202                               last OE;                               last OE;
2203                             }                             }
2204                           } # OE                           } # OE
2205                           $foster_parent_element = $open_elements->[0]->[0]                           $foster_parent_element = $self->{open_elements}->[0]->[0]
2206                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
2207                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
2208                             ($child, $next_sibling);                             ($child, $next_sibling);
2209                         } else {                         } else {
2210                           $open_elements->[-1]->[0]->append_child ($child);                           $self->{open_elements}->[-1]->[0]->append_child ($child);
2211                         }                         }
2212    }; # $insert_to_foster    }; # $insert_to_foster
2213    
# Line 2045  sub _construct_tree ($) { Line 2223  sub _construct_tree ($) {
2223        } elsif ({        } elsif ({
2224                  base => 1, link => 1, meta => 1,                  base => 1, link => 1, meta => 1,
2225                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2226          !!!parse-error ($token->{tag_name}.' in body');          !!!parse-error (type => 'in body:'.$token->{tag_name});
2227          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
2228          my $el;          my $el;
2229          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes});
2230          if (defined $head_element) {          if (defined $self->{head_element}) {
2231            $head_element->append_child ($el);            $self->{head_element}->append_child ($el);
2232          } else {          } else {
2233            $insert->($el);            $insert->($el);
2234          }          }
# Line 2058  sub _construct_tree ($) { Line 2236  sub _construct_tree ($) {
2236          !!!next-token;          !!!next-token;
2237          return;          return;
2238        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
2239          !!!parse-error ('title in body');          !!!parse-error (type => 'in body:title');
2240          ## NOTE: There is an "as if in head" code clone          ## NOTE: There is an "as if in head" code clone
2241          my $title_el;          my $title_el;
2242          !!!create-element ($title_el, 'title', $token->{attributes});          !!!create-element ($title_el, 'title', $token->{attributes});
2243          (defined $head_element ? $head_element : $open_elements->[-1]->[0])          (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
2244            ->append_child ($title_el);            ->append_child ($title_el);
2245          $self->{content_model_flag} = 'RCDATA';          $self->{content_model_flag} = 'RCDATA';
2246                    
# Line 2082  sub _construct_tree ($) { Line 2260  sub _construct_tree ($) {
2260              $token->{tag_name} eq 'title') {              $token->{tag_name} eq 'title') {
2261            ## Ignore the token            ## Ignore the token
2262          } else {          } else {
2263            !!!parse-error;            !!!parse-error (type => 'in RCDATA:#'.$token->{type});
2264            ## ISSUE: And ignore?            ## ISSUE: And ignore?
2265          }          }
2266          !!!next-token;          !!!next-token;
2267          return;          return;
2268        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
2269          !!!parse-error;          !!!parse-error (type => 'in body:body');
2270                                
2271          if (@$open_elements == 1 or          if (@{$self->{open_elements}} == 1 or
2272              $open_elements->[1]->[1] ne 'body') {              $self->{open_elements}->[1]->[1] ne 'body') {
2273            ## Ignore the token            ## Ignore the token
2274          } else {          } else {
2275            my $body_el = $open_elements->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
2276            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
2277              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
2278                $body_el->set_attribute_ns                $body_el->set_attribute_ns
# Line 2112  sub _construct_tree ($) { Line 2290  sub _construct_tree ($) {
2290                  pre => 1,                  pre => 1,
2291                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2292          ## has a p element in scope          ## has a p element in scope
2293          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2294            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2295              !!!back-token;              !!!back-token;
2296              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2139  sub _construct_tree ($) { Line 2317  sub _construct_tree ($) {
2317          }          }
2318          return;          return;
2319        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
2320          if (defined $form_element) {          if (defined $self->{form_element}) {
2321            !!!parse-error;            !!!parse-error (type => 'in form:form');
2322            ## Ignore the token            ## Ignore the token
2323          } else {          } else {
2324            ## has a p element in scope            ## has a p element in scope
2325            INSCOPE: for (reverse @$open_elements) {            INSCOPE: for (reverse @{$self->{open_elements}}) {
2326              if ($_->[1] eq 'p') {              if ($_->[1] eq 'p') {
2327                !!!back-token;                !!!back-token;
2328                $token = {type => 'end tag', tag_name => 'p'};                $token = {type => 'end tag', tag_name => 'p'};
# Line 2158  sub _construct_tree ($) { Line 2336  sub _construct_tree ($) {
2336            } # INSCOPE            } # INSCOPE
2337                            
2338            !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2339            $form_element = $open_elements->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
2340            !!!next-token;            !!!next-token;
2341            return;            return;
2342          }          }
2343        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
2344          ## has a p element in scope          ## has a p element in scope
2345          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2346            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2347              !!!back-token;              !!!back-token;
2348              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2179  sub _construct_tree ($) { Line 2357  sub _construct_tree ($) {
2357                        
2358          ## Step 1          ## Step 1
2359          my $i = -1;          my $i = -1;
2360          my $node = $open_elements->[$i];          my $node = $self->{open_elements}->[$i];
2361          LI: {          LI: {
2362            ## Step 2            ## Step 2
2363            if ($node->[1] eq 'li') {            if ($node->[1] eq 'li') {
2364              splice @$open_elements, $i;              splice @{$self->{open_elements}}, $i;
2365              last LI;              last LI;
2366            }            }
2367                        
# Line 2198  sub _construct_tree ($) { Line 2376  sub _construct_tree ($) {
2376                        
2377            ## Step 4            ## Step 4
2378            $i--;            $i--;
2379            $node = $open_elements->[$i];            $node = $self->{open_elements}->[$i];
2380            redo LI;            redo LI;
2381          } # LI          } # LI
2382                        
# Line 2207  sub _construct_tree ($) { Line 2385  sub _construct_tree ($) {
2385          return;          return;
2386        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {
2387          ## has a p element in scope          ## has a p element in scope
2388          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2389            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2390              !!!back-token;              !!!back-token;
2391              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2222  sub _construct_tree ($) { Line 2400  sub _construct_tree ($) {
2400                        
2401          ## Step 1          ## Step 1
2402          my $i = -1;          my $i = -1;
2403          my $node = $open_elements->[$i];          my $node = $self->{open_elements}->[$i];
2404          LI: {          LI: {
2405            ## Step 2            ## Step 2
2406            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {
2407              splice @$open_elements, $i;              splice @{$self->{open_elements}}, $i;
2408              last LI;              last LI;
2409            }            }
2410                        
# Line 2241  sub _construct_tree ($) { Line 2419  sub _construct_tree ($) {
2419                        
2420            ## Step 4            ## Step 4
2421            $i--;            $i--;
2422            $node = $open_elements->[$i];            $node = $self->{open_elements}->[$i];
2423            redo LI;            redo LI;
2424          } # LI          } # LI
2425                        
# Line 2250  sub _construct_tree ($) { Line 2428  sub _construct_tree ($) {
2428          return;          return;
2429        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
2430          ## has a p element in scope          ## has a p element in scope
2431          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2432            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2433              !!!back-token;              !!!back-token;
2434              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2273  sub _construct_tree ($) { Line 2451  sub _construct_tree ($) {
2451                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2452                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2453          ## has a p element in scope          ## has a p element in scope
2454          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2455            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2456            if ($node->[1] eq 'p') {            if ($node->[1] eq 'p') {
2457              !!!back-token;              !!!back-token;
2458              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2289  sub _construct_tree ($) { Line 2467  sub _construct_tree ($) {
2467                        
2468          ## has an element in scope          ## has an element in scope
2469          my $i;          my $i;
2470          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2471            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2472            if ({            if ({
2473                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2474                }->{$node->[1]}) {                }->{$node->[1]}) {
# Line 2305  sub _construct_tree ($) { Line 2483  sub _construct_tree ($) {
2483          } # INSCOPE          } # INSCOPE
2484                        
2485          if (defined $i) {          if (defined $i) {
2486            !!!parse-error;            !!!parse-error (type => 'in hn:hn');
2487            splice @$open_elements, $i;            splice @{$self->{open_elements}}, $i;
2488          }          }
2489                        
2490          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
# Line 2317  sub _construct_tree ($) { Line 2495  sub _construct_tree ($) {
2495          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
2496            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
2497            if ($node->[1] eq 'a') {            if ($node->[1] eq 'a') {
2498              !!!parse-error ('a in a');              !!!parse-error (type => 'in a:a');
2499                            
2500              !!!back-token;              !!!back-token;
2501              $token = {type => 'end tag', tag_name => 'a'};              $token = {type => 'end tag', tag_name => 'a'};
# Line 2329  sub _construct_tree ($) { Line 2507  sub _construct_tree ($) {
2507                  last AFE2;                  last AFE2;
2508                }                }
2509              } # AFE2              } # AFE2
2510              OE: for (reverse 0..$#$open_elements) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
2511                if ($open_elements->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
2512                  splice @$open_elements, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
2513                  last OE;                  last OE;
2514                }                }
2515              } # OE              } # OE
# Line 2344  sub _construct_tree ($) { Line 2522  sub _construct_tree ($) {
2522          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2523    
2524          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2525          push @$active_formatting_elements, $open_elements->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
2526    
2527          !!!next-token;          !!!next-token;
2528          return;          return;
# Line 2356  sub _construct_tree ($) { Line 2534  sub _construct_tree ($) {
2534          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2535                    
2536          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2537          push @$active_formatting_elements, $open_elements->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
2538                    
2539          !!!next-token;          !!!next-token;
2540          return;          return;
2541        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
2542          ## has a button element in scope          ## has a button element in scope
2543          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2544            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2545            if ($node->[1] eq 'button') {            if ($node->[1] eq 'button') {
2546              !!!parse-error;              !!!parse-error (type => 'in button:button');
2547              !!!back-token;              !!!back-token;
2548              $token = {type => 'end tag', tag_name => 'button'};              $token = {type => 'end tag', tag_name => 'button'};
2549              return;              return;
# Line 2404  sub _construct_tree ($) { Line 2582  sub _construct_tree ($) {
2582          return;          return;
2583        } elsif ($token->{tag_name} eq 'table') {        } elsif ($token->{tag_name} eq 'table') {
2584          ## has a p element in scope          ## has a p element in scope
2585          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2586            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2587              !!!back-token;              !!!back-token;
2588              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2419  sub _construct_tree ($) { Line 2597  sub _construct_tree ($) {
2597                        
2598          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2599                        
2600          $insertion_mode = 'in table';          $self->{insertion_mode} = 'in table';
2601                        
2602          !!!next-token;          !!!next-token;
2603          return;          return;
# Line 2429  sub _construct_tree ($) { Line 2607  sub _construct_tree ($) {
2607                  image => 1,                  image => 1,
2608                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2609          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
2610            !!!parse-error;            !!!parse-error (type => 'image');
2611            $token->{tag_name} = 'img';            $token->{tag_name} = 'img';
2612          }          }
2613                    
2614          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2615                    
2616          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2617          pop @$open_elements;          pop @{$self->{open_elements}};
2618                    
2619          !!!next-token;          !!!next-token;
2620          return;          return;
2621        } elsif ($token->{tag_name} eq 'hr') {        } elsif ($token->{tag_name} eq 'hr') {
2622          ## has a p element in scope          ## has a p element in scope
2623          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2624            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2625              !!!back-token;              !!!back-token;
2626              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2456  sub _construct_tree ($) { Line 2634  sub _construct_tree ($) {
2634          } # INSCOPE          } # INSCOPE
2635                        
2636          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2637          pop @$open_elements;          pop @{$self->{open_elements}};
2638                        
2639          !!!next-token;          !!!next-token;
2640          return;          return;
# Line 2464  sub _construct_tree ($) { Line 2642  sub _construct_tree ($) {
2642          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2643                    
2644          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2645          ## TODO: associate with $form_element if defined          ## TODO: associate with $self->{form_element} if defined
2646          pop @$open_elements;          pop @{$self->{open_elements}};
2647                    
2648          !!!next-token;          !!!next-token;
2649          return;          return;
2650        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
2651          !!!parse-error;          !!!parse-error (type => 'isindex');
2652                    
2653          if (defined $form_element) {          if (defined $self->{form_element}) {
2654            ## Ignore the token            ## Ignore the token
2655            !!!next-token;            !!!next-token;
2656            return;            return;
# Line 2500  sub _construct_tree ($) { Line 2678  sub _construct_tree ($) {
2678          }          }
2679        } elsif ({        } elsif ({
2680                  textarea => 1,                  textarea => 1,
2681                    iframe => 1,
2682                  noembed => 1,                  noembed => 1,
2683                  noframes => 1,                  noframes => 1,
2684                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
# Line 2509  sub _construct_tree ($) { Line 2688  sub _construct_tree ($) {
2688          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes});
2689                    
2690          if ($token->{tag_name} eq 'textarea') {          if ($token->{tag_name} eq 'textarea') {
2691            ## TODO: form_element if defined            ## TODO: $self->{form_element} if defined
2692            $self->{content_model_flag} = 'RCDATA';            $self->{content_model_flag} = 'RCDATA';
2693          } else {          } else {
2694            $self->{content_model_flag} = 'CDATA';            $self->{content_model_flag} = 'CDATA';
# Line 2533  sub _construct_tree ($) { Line 2712  sub _construct_tree ($) {
2712              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
2713            ## Ignore the token            ## Ignore the token
2714          } else {          } else {
2715            !!!parse-error;            if ($token->{tag_name} eq 'textarea') {
2716                !!!parse-error (type => 'in CDATA:#'.$token->{type});
2717              } else {
2718                !!!parse-error (type => 'in RCDATA:#'.$token->{type});
2719              }
2720            ## ISSUE: And ignore?            ## ISSUE: And ignore?
2721          }          }
2722          !!!next-token;          !!!next-token;
# Line 2543  sub _construct_tree ($) { Line 2726  sub _construct_tree ($) {
2726                    
2727          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2728                    
2729          $insertion_mode = 'in select';          $self->{insertion_mode} = 'in select';
2730          !!!next-token;          !!!next-token;
2731          return;          return;
2732        } elsif ({        } elsif ({
# Line 2552  sub _construct_tree ($) { Line 2735  sub _construct_tree ($) {
2735                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
2736                  thead => 1, tr => 1,                  thead => 1, tr => 1,
2737                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2738          !!!parse-error;          !!!parse-error (type => 'in body:'.$token->{tag_name});
2739          ## Ignore the token          ## Ignore the token
2740          !!!next-token;          !!!next-token;
2741          return;          return;
# Line 2568  sub _construct_tree ($) { Line 2751  sub _construct_tree ($) {
2751        }        }
2752      } elsif ($token->{type} eq 'end tag') {      } elsif ($token->{type} eq 'end tag') {
2753        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
2754          if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
2755            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
2756            if ($open_elements->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
2757              !!!parse-error;              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2758            }            }
2759            $insertion_mode = 'after body';            $self->{insertion_mode} = 'after body';
2760            !!!next-token;            !!!next-token;
2761            return;            return;
2762          } else {          } else {
2763            !!!parse-error;            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2764            ## Ignore the token            ## Ignore the token
2765            !!!next-token;            !!!next-token;
2766            return;            return;
2767          }          }
2768        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
2769          if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
2770            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
2771            if ($open_elements->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
2772              !!!parse-error;              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);
2773            }            }
2774            $insertion_mode = 'after body';            $self->{insertion_mode} = 'after body';
2775            ## reprocess            ## reprocess
2776            return;            return;
2777          } else {          } else {
2778            !!!parse-error;            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2779            ## Ignore the token            ## Ignore the token
2780            !!!next-token;            !!!next-token;
2781            return;            return;
# Line 2608  sub _construct_tree ($) { Line 2791  sub _construct_tree ($) {
2791                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2792          ## has an element in scope          ## has an element in scope
2793          my $i;          my $i;
2794          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2795            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2796            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] eq $token->{tag_name}) {
2797              ## generate implied end tags              ## generate implied end tags
2798              if ({              if ({
# Line 2618  sub _construct_tree ($) { Line 2801  sub _construct_tree ($) {
2801                   li => ($token->{tag_name} ne 'li'),                   li => ($token->{tag_name} ne 'li'),
2802                   p => ($token->{tag_name} ne 'p'),                   p => ($token->{tag_name} ne 'p'),
2803                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2804                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2805                !!!back-token;                !!!back-token;
2806                $token = {type => 'end tag',                $token = {type => 'end tag',
2807                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2808                return;                return;
2809              }              }
2810              $i = $_;              $i = $_;
# Line 2634  sub _construct_tree ($) { Line 2817  sub _construct_tree ($) {
2817            }            }
2818          } # INSCOPE          } # INSCOPE
2819                    
2820          if ($open_elements->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
2821            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2822          }          }
2823                    
2824          splice @$open_elements, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
2825          undef $form_element if $token->{tag_name} eq 'form';          undef $self->{form_element} if $token->{tag_name} eq 'form';
2826          $clear_up_to_marker->()          $clear_up_to_marker->()
2827            if {            if {
2828              button => 1, marquee => 1, object => 1,              button => 1, marquee => 1, object => 1,
# Line 2651  sub _construct_tree ($) { Line 2834  sub _construct_tree ($) {
2834                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2835          ## has an element in scope          ## has an element in scope
2836          my $i;          my $i;
2837          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2838            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2839            if ({            if ({
2840                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2841                }->{$node->[1]}) {                }->{$node->[1]}) {
# Line 2660  sub _construct_tree ($) { Line 2843  sub _construct_tree ($) {
2843              if ({              if ({
2844                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
2845                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2846                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2847                !!!back-token;                !!!back-token;
2848                $token = {type => 'end tag',                $token = {type => 'end tag',
2849                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2850                return;                return;
2851              }              }
2852              $i = $_;              $i = $_;
# Line 2676  sub _construct_tree ($) { Line 2859  sub _construct_tree ($) {
2859            }            }
2860          } # INSCOPE          } # INSCOPE
2861                    
2862          if ($open_elements->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
2863            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2864          }          }
2865                    
2866          splice @$open_elements, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
2867          !!!next-token;          !!!next-token;
2868          return;          return;
2869        } elsif ({        } elsif ({
# Line 2698  sub _construct_tree ($) { Line 2881  sub _construct_tree ($) {
2881                  thead => 1, tr => 1,                  thead => 1, tr => 1,
2882                  area => 1, basefont => 1, bgsound => 1, br => 1,                  area => 1, basefont => 1, bgsound => 1, br => 1,
2883                  embed => 1, hr => 1, iframe => 1, image => 1,                  embed => 1, hr => 1, iframe => 1, image => 1,
2884                  img => 1, input => 1, isindex=> 1, noembed => 1,                  img => 1, input => 1, isindex => 1, noembed => 1,
2885                  noframes => 1, param => 1, select => 1, spacer => 1,                  noframes => 1, param => 1, select => 1, spacer => 1,
2886                  table => 1, textarea => 1, wbr => 1,                  table => 1, textarea => 1, wbr => 1,
2887                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
2888                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2889          !!!parse-error;          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2890          ## Ignore the token          ## Ignore the token
2891          !!!next-token;          !!!next-token;
2892          return;          return;
# Line 2713  sub _construct_tree ($) { Line 2896  sub _construct_tree ($) {
2896        } else {        } else {
2897          ## Step 1          ## Step 1
2898          my $node_i = -1;          my $node_i = -1;
2899          my $node = $open_elements->[$node_i];          my $node = $self->{open_elements}->[$node_i];
2900    
2901          ## Step 2          ## Step 2
2902          S2: {          S2: {
# Line 2723  sub _construct_tree ($) { Line 2906  sub _construct_tree ($) {
2906              if ({              if ({
2907                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
2908                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2909                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2910                !!!back-token;                !!!back-token;
2911                $token = {type => 'end tag',                $token = {type => 'end tag',
2912                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2913                return;                return;
2914              }              }
2915                    
2916              ## Step 2              ## Step 2
2917              if ($token->{tag_name} ne $open_elements->[-1]->[1]) {              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
2918                !!!parse-error;                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2919              }              }
2920                            
2921              ## Step 3              ## Step 3
2922              splice @$open_elements, $node_i;              splice @{$self->{open_elements}}, $node_i;
2923    
2924                !!!next-token;
2925              last S2;              last S2;
2926            } else {            } else {
2927              ## Step 3              ## Step 3
# Line 2744  sub _construct_tree ($) { Line 2929  sub _construct_tree ($) {
2929                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
2930                  ($special_category->{$node->[1]} or                  ($special_category->{$node->[1]} or
2931                   $scoping_category->{$node->[1]})) {                   $scoping_category->{$node->[1]})) {
2932                !!!parse-error;                !!!parse-error (type => 'not closed:'.$node->[1]);
2933                ## Ignore the token                ## Ignore the token
2934                !!!next-token;                !!!next-token;
2935                last S2;                last S2;
# Line 2753  sub _construct_tree ($) { Line 2938  sub _construct_tree ($) {
2938                        
2939            ## Step 4            ## Step 4
2940            $node_i--;            $node_i--;
2941            $node = $open_elements->[$node_i];            $node = $self->{open_elements}->[$node_i];
2942                        
2943            ## Step 5;            ## Step 5;
2944            redo S2;            redo S2;
2945          } # S2          } # S2
2946            return;
2947        }        }
2948      }      }
2949    }; # $in_body    }; # $in_body
2950    
2951    B: {    B: {
2952      if ($phase eq 'initial') {      if ($phase eq 'main') {
       if ($token->{type} eq 'DOCTYPE') {  
         if ($token->{error}) {  
           ## ISSUE: Spec currently left this case undefined.  
           !!!parse-error ('bogus DOCTYPE');  
         }  
         my $doctype = $self->{document}->create_document_type_definition  
           ($token->{name});  
         $self->{document}->append_child ($doctype);  
         $phase = 'root element';  
         !!!next-token;  
         redo B;  
       } elsif ({  
                 comment => 1,  
                 'start tag' => 1,  
                 'end tag' => 1,  
                 'end-of-file' => 1,  
                }->{$token->{type}}) {  
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error ('missing DOCTYPE');  
         $phase = 'root element';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'character') {  
         if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
           $self->{document}->manakai_append_text ($1);  
           ## ISSUE: DOM3 Core does not allow Document > Text  
           unless (length $token->{data}) {  
             ## Stay in the phase  
             !!!next-token;  
             redo B;  
           }  
         }  
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error ('missing DOCTYPE');  
         $phase = 'root element';  
         ## reprocess  
         redo B;  
       } else {  
         die "$0: $token->{type}: Unknown token";  
       }  
     } elsif ($phase eq 'root element') {  
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error;  
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'comment') {  
         my $comment = $self->{document}->create_comment ($token->{data});  
         $self->{document}->append_child ($comment);  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'character') {  
         if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
           $self->{document}->manakai_append_text ($1);  
           ## ISSUE: DOM3 Core does not allow Document > Text  
           unless (length $token->{data}) {  
             ## Stay in the phase  
             !!!next-token;  
             redo B;  
           }  
         }  
         #  
       } elsif ({  
                 'start tag' => 1,  
                 'end tag' => 1,  
                 'end-of-file' => 1,  
                }->{$token->{type}}) {  
         ## ISSUE: There is an issue in the spec  
         #  
       } else {  
         die "$0: $token->{type}: Unknown token";  
       }  
       my $root_element; !!!create-element ($root_element, 'html');  
       $self->{document}->append_child ($root_element);  
       $open_elements = [[$root_element, 'html']];  
       $phase = 'main';  
       ## reprocess  
       redo B;  
     } elsif ($phase eq 'main') {  
2953        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} eq 'DOCTYPE') {
2954          !!!parse-error;          !!!parse-error (type => 'in html:#DOCTYPE');
2955          ## Ignore the token          ## Ignore the token
2956          ## Stay in the phase          ## Stay in the phase
2957          !!!next-token;          !!!next-token;
# Line 2854  sub _construct_tree ($) { Line 2959  sub _construct_tree ($) {
2959        } elsif ($token->{type} eq 'start tag' and        } elsif ($token->{type} eq 'start tag' and
2960                 $token->{tag_name} eq 'html') {                 $token->{tag_name} eq 'html') {
2961          ## TODO: unless it is the first start tag token, parse-error          ## TODO: unless it is the first start tag token, parse-error
2962          my $top_el = $open_elements->[0]->[0];          my $top_el = $self->{open_elements}->[0]->[0];
2963          for my $attr_name (keys %{$token->{attributes}}) {          for my $attr_name (keys %{$token->{attributes}}) {
2964            unless ($top_el->has_attribute_ns (undef, $attr_name)) {            unless ($top_el->has_attribute_ns (undef, $attr_name)) {
2965              $top_el->set_attribute_ns              $top_el->set_attribute_ns
# Line 2868  sub _construct_tree ($) { Line 2973  sub _construct_tree ($) {
2973          ## Generate implied end tags          ## Generate implied end tags
2974          if ({          if ({
2975               dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,               dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,
2976              }->{$open_elements->[-1]->[1]}) {              }->{$self->{open_elements}->[-1]->[1]}) {
2977            !!!back-token;            !!!back-token;
2978            $token = {type => 'end tag', tag_name => $open_elements->[-1]->[1]};            $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};
2979            redo B;            redo B;
2980          }          }
2981                    
2982          if (@$open_elements > 2 or          if (@{$self->{open_elements}} > 2 or
2983              (@$open_elements == 2 and $open_elements->[1]->[1] ne 'body')) {              (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {
2984            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2985          } else {          } elsif (defined $self->{inner_html_node} and
2986            ## TODO: inner_html parser and @$open_elements > 1 and $open_elements->[1] ne 'body', then parse-error                   @{$self->{open_elements}} > 1 and
2987                     $self->{open_elements}->[1]->[1] ne 'body') {
2988              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2989          }          }
2990    
2991          ## Stop parsing          ## Stop parsing
# Line 2886  sub _construct_tree ($) { Line 2993  sub _construct_tree ($) {
2993    
2994          ## ISSUE: There is an issue in the spec.          ## ISSUE: There is an issue in the spec.
2995        } else {        } else {
2996          if ($insertion_mode eq 'before head') {          if ($self->{insertion_mode} eq 'before head') {
2997            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
2998              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
2999                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3000                unless (length $token->{data}) {                unless (length $token->{data}) {
3001                  !!!next-token;                  !!!next-token;
3002                  redo B;                  redo B;
3003                }                }
3004              }              }
3005              ## As if <head>              ## As if <head>
3006              !!!create-element ($head_element, 'head');              !!!create-element ($self->{head_element}, 'head');
3007              $open_elements->[-1]->[0]->append_child ($head_element);              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3008              push @$open_elements, [$head_element, 'head'];              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3009              $insertion_mode = 'in head';              $self->{insertion_mode} = 'in head';
3010              ## reprocess              ## reprocess
3011              redo B;              redo B;
3012            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3013              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3014              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3015              !!!next-token;              !!!next-token;
3016              redo B;              redo B;
3017            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
3018              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};
3019              !!!create-element ($head_element, 'head', $attr);              !!!create-element ($self->{head_element}, 'head', $attr);
3020              $open_elements->[-1]->[0]->append_child ($head_element);              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3021              push @$open_elements, [$head_element, 'head'];              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3022              $insertion_mode = 'in head';              $self->{insertion_mode} = 'in head';
3023              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
3024                !!!next-token;                !!!next-token;
3025              #} elsif ({              #} elsif ({
# Line 2927  sub _construct_tree ($) { Line 3034  sub _construct_tree ($) {
3034            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
3035              if ($token->{tag_name} eq 'html') {              if ($token->{tag_name} eq 'html') {
3036                ## As if <head>                ## As if <head>
3037                !!!create-element ($head_element, 'head');                !!!create-element ($self->{head_element}, 'head');
3038                $open_elements->[-1]->[0]->append_child ($head_element);                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
3039                push @$open_elements, [$head_element, 'head'];                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
3040                $insertion_mode = 'in head';                $self->{insertion_mode} = 'in head';
3041                ## reprocess                ## reprocess
3042                redo B;                redo B;
3043              } else {              } else {
3044                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3045                ## Ignore the token                ## Ignore the token
3046                !!!next-token;                !!!next-token;
3047                redo B;                redo B;
# Line 2942  sub _construct_tree ($) { Line 3049  sub _construct_tree ($) {
3049            } else {            } else {
3050              die "$0: $token->{type}: Unknown type";              die "$0: $token->{type}: Unknown type";
3051            }            }
3052          } elsif ($insertion_mode eq 'in head') {          } elsif ($self->{insertion_mode} eq 'in head') {
3053            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3054              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3055                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3056                unless (length $token->{data}) {                unless (length $token->{data}) {
3057                  !!!next-token;                  !!!next-token;
3058                  redo B;                  redo B;
# Line 2955  sub _construct_tree ($) { Line 3062  sub _construct_tree ($) {
3062              #              #
3063            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3064              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3065              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3066              !!!next-token;              !!!next-token;
3067              redo B;              redo B;
3068            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 2963  sub _construct_tree ($) { Line 3070  sub _construct_tree ($) {
3070                ## NOTE: There is an "as if in head" code clone                ## NOTE: There is an "as if in head" code clone
3071                my $title_el;                my $title_el;
3072                !!!create-element ($title_el, 'title', $token->{attributes});                !!!create-element ($title_el, 'title', $token->{attributes});
3073                (defined $head_element ? $head_element : $open_elements->[-1]->[0])                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
3074                  ->append_child ($title_el);                  ->append_child ($title_el);
3075                $self->{content_model_flag} = 'RCDATA';                $self->{content_model_flag} = 'RCDATA';
3076    
# Line 2983  sub _construct_tree ($) { Line 3090  sub _construct_tree ($) {
3090                    $token->{tag_name} eq 'title') {                    $token->{tag_name} eq 'title') {
3091                  ## Ignore the token                  ## Ignore the token
3092                } else {                } else {
3093                  !!!parse-error;                  !!!parse-error (type => 'in RCDATA:#'.$token->{type});
3094                  ## ISSUE: And ignore?                  ## ISSUE: And ignore?
3095                }                }
3096                !!!next-token;                !!!next-token;
# Line 2998  sub _construct_tree ($) { Line 3105  sub _construct_tree ($) {
3105                ## NOTE: There are "as if in head" code clones                ## NOTE: There are "as if in head" code clones
3106                my $el;                my $el;
3107                !!!create-element ($el, $token->{tag_name}, $token->{attributes});                !!!create-element ($el, $token->{tag_name}, $token->{attributes});
3108                (defined $head_element ? $head_element : $open_elements->[-1]->[0])                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
3109                  ->append_child ($el);                  ->append_child ($el);
3110    
3111                !!!next-token;                !!!next-token;
3112                redo B;                redo B;
3113              } elsif ($token->{tag_name} eq 'head') {              } elsif ($token->{tag_name} eq 'head') {
3114                !!!parse-error;                !!!parse-error (type => 'in head:head');
3115                ## Ignore the token                ## Ignore the token
3116                !!!next-token;                !!!next-token;
3117                redo B;                redo B;
# Line 3013  sub _construct_tree ($) { Line 3120  sub _construct_tree ($) {
3120              }              }
3121            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
3122              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
3123                if ($open_elements->[-1]->[1] eq 'head') {                if ($self->{open_elements}->[-1]->[1] eq 'head') {
3124                  pop @$open_elements;                  pop @{$self->{open_elements}};
3125                } else {                } else {
3126                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:head');
3127                }                }
3128                $insertion_mode = 'after head';                $self->{insertion_mode} = 'after head';
3129                !!!next-token;                !!!next-token;
3130                redo B;                redo B;
3131              } elsif ($token->{tag_name} eq 'html') {              } elsif ($token->{tag_name} eq 'html') {
3132                #                #
3133              } else {              } else {
3134                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3135                ## Ignore the token                ## Ignore the token
3136                !!!next-token;                !!!next-token;
3137                redo B;                redo B;
# Line 3033  sub _construct_tree ($) { Line 3140  sub _construct_tree ($) {
3140              #              #
3141            }            }
3142    
3143            if ($open_elements->[-1]->[1] eq 'head') {            if ($self->{open_elements}->[-1]->[1] eq 'head') {
3144              ## As if </head>              ## As if </head>
3145              pop @$open_elements;              pop @{$self->{open_elements}};
3146            }            }
3147            $insertion_mode = 'after head';            $self->{insertion_mode} = 'after head';
3148            ## reprocess            ## reprocess
3149            redo B;            redo B;
3150    
3151            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
3152          } elsif ($insertion_mode eq 'after head') {          } elsif ($self->{insertion_mode} eq 'after head') {
3153            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3154              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3155                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3156                unless (length $token->{data}) {                unless (length $token->{data}) {
3157                  !!!next-token;                  !!!next-token;
3158                  redo B;                  redo B;
# Line 3055  sub _construct_tree ($) { Line 3162  sub _construct_tree ($) {
3162              #              #
3163            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3164              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3165              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3166              !!!next-token;              !!!next-token;
3167              redo B;              redo B;
3168            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
3169              if ($token->{tag_name} eq 'body') {              if ($token->{tag_name} eq 'body') {
3170                !!!insert-element ('body', $token->{attributes});                !!!insert-element ('body', $token->{attributes});
3171                $insertion_mode = 'in body';                $self->{insertion_mode} = 'in body';
3172                !!!next-token;                !!!next-token;
3173                redo B;                redo B;
3174              } elsif ($token->{tag_name} eq 'frameset') {              } elsif ($token->{tag_name} eq 'frameset') {
3175                !!!insert-element ('frameset', $token->{attributes});                !!!insert-element ('frameset', $token->{attributes});
3176                $insertion_mode = 'in frameset';                $self->{insertion_mode} = 'in frameset';
3177                !!!next-token;                !!!next-token;
3178                redo B;                redo B;
3179              } elsif ({              } elsif ({
3180                        base => 1, link => 1, meta => 1,                        base => 1, link => 1, meta => 1,
3181                        script=> 1, style => 1, title => 1,                        script => 1, style => 1, title => 1,
3182                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3183                !!!parse-error;                !!!parse-error (type => 'after head:'.$token->{tag_name});
3184                 $insertion_mode = 'in head';                $self->{insertion_mode} = 'in head';
3185                ## reprocess                ## reprocess
3186                redo B;                redo B;
3187              } else {              } else {
# Line 3086  sub _construct_tree ($) { Line 3193  sub _construct_tree ($) {
3193                        
3194            ## As if <body>            ## As if <body>
3195            !!!insert-element ('body');            !!!insert-element ('body');
3196            $insertion_mode = 'in body';            $self->{insertion_mode} = 'in body';
3197            ## reprocess            ## reprocess
3198            redo B;            redo B;
3199          } elsif ($insertion_mode eq 'in body') {          } elsif ($self->{insertion_mode} eq 'in body') {
3200            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3201              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
3202              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
3203                            
3204              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3205    
3206              !!!next-token;              !!!next-token;
3207              redo B;              redo B;
3208            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3209              ## NOTE: There is a code clone of "comment in body".              ## NOTE: There is a code clone of "comment in body".
3210              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3211              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3212              !!!next-token;              !!!next-token;
3213              redo B;              redo B;
3214            } else {            } else {
3215              $in_body->($insert_to_current);              $in_body->($insert_to_current);
3216              redo B;              redo B;
3217            }            }
3218          } elsif ($insertion_mode eq 'in table') {          } elsif ($self->{insertion_mode} eq 'in table') {
3219            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3220              ## NOTE: There are "character in table" code clones.              ## NOTE: There are "character in table" code clones.
3221              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3222                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3223                                
3224                unless (length $token->{data}) {                unless (length $token->{data}) {
3225                  !!!next-token;                  !!!next-token;
# Line 3120  sub _construct_tree ($) { Line 3227  sub _construct_tree ($) {
3227                }                }
3228              }              }
3229    
3230                !!!parse-error (type => 'in table:#character');
3231    
3232              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3233              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3234              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3129  sub _construct_tree ($) { Line 3238  sub _construct_tree ($) {
3238              if ({              if ({
3239                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3240                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3241                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3242                # MUST                # MUST
3243                my $foster_parent_element;                my $foster_parent_element;
3244                my $next_sibling;                my $next_sibling;
3245                my $prev_sibling;                my $prev_sibling;
3246                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3247                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3248                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3249                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3250                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3251                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3252                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
3253                    } else {                    } else {
3254                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3255                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
3256                    }                    }
3257                    last OE;                    last OE;
3258                  }                  }
3259                } # OE                } # OE
3260                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
3261                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
3262                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
3263                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3160  sub _construct_tree ($) { Line 3269  sub _construct_tree ($) {
3269                     $next_sibling);                     $next_sibling);
3270                }                }
3271              } else {              } else {
3272                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3273              }              }
3274                            
3275              !!!next-token;              !!!next-token;
3276              redo B;              redo B;
3277            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3278              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3279              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3280              !!!next-token;              !!!next-token;
3281              redo B;              redo B;
3282            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3177  sub _construct_tree ($) { Line 3286  sub _construct_tree ($) {
3286                   tbody => 1, tfoot => 1, thead => 1,                   tbody => 1, tfoot => 1, thead => 1,
3287                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3288                ## Clear back to table context                ## Clear back to table context
3289                while ($open_elements->[-1]->[1] ne 'table' and                while ($self->{open_elements}->[-1]->[1] ne 'table' and
3290                       $open_elements->[-1]->[1] ne 'html') {                       $self->{open_elements}->[-1]->[1] ne 'html') {
3291                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3292                  pop @$open_elements;                  pop @{$self->{open_elements}};
3293                }                }
3294    
3295                push @$active_formatting_elements, ['#marker', '']                push @$active_formatting_elements, ['#marker', '']
3296                  if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3297    
3298                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3299                $insertion_mode = {                $self->{insertion_mode} = {
3300                                   caption => 'in caption',                                   caption => 'in caption',
3301                                   colgroup => 'in column group',                                   colgroup => 'in column group',
3302                                   tbody => 'in table body',                                   tbody => 'in table body',
# Line 3201  sub _construct_tree ($) { Line 3310  sub _construct_tree ($) {
3310                        td => 1, th => 1, tr => 1,                        td => 1, th => 1, tr => 1,
3311                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3312                ## Clear back to table context                ## Clear back to table context
3313                while ($open_elements->[-1]->[1] ne 'table' and                while ($self->{open_elements}->[-1]->[1] ne 'table' and
3314                       $open_elements->[-1]->[1] ne 'html') {                       $self->{open_elements}->[-1]->[1] ne 'html') {
3315                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3316                  pop @$open_elements;                  pop @{$self->{open_elements}};
3317                }                }
3318    
3319                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');
3320                $insertion_mode = $token->{tag_name} eq 'col'                $self->{insertion_mode} = $token->{tag_name} eq 'col'
3321                  ? 'in column group' : 'in table body';                  ? 'in column group' : 'in table body';
3322                ## reprocess                ## reprocess
3323                redo B;                redo B;
3324              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3325                ## NOTE: There are code clones for this "table in table"                ## NOTE: There are code clones for this "table in table"
3326                !!!parse-error;                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3327    
3328                ## As if </table>                ## As if </table>
3329                ## have a table element in table scope                ## have a table element in table scope
3330                my $i;                my $i;
3331                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3332                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3333                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
3334                    $i = $_;                    $i = $_;
3335                    last INSCOPE;                    last INSCOPE;
# Line 3231  sub _construct_tree ($) { Line 3340  sub _construct_tree ($) {
3340                  }                  }
3341                } # INSCOPE                } # INSCOPE
3342                unless (defined $i) {                unless (defined $i) {
3343                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
3344                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
3345                  !!!next-token;                  !!!next-token;
3346                  redo B;                  redo B;
# Line 3241  sub _construct_tree ($) { Line 3350  sub _construct_tree ($) {
3350                if ({                if ({
3351                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3352                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3353                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3354                  !!!back-token; # <table>                  !!!back-token; # <table>
3355                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
3356                  !!!back-token;                  !!!back-token;
3357                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3358                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3359                  redo B;                  redo B;
3360                }                }
3361    
3362                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3363                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3364                }                }
3365    
3366                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3367    
3368                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
3369    
3370                ## reprocess                ## reprocess
3371                redo B;                redo B;
# Line 3267  sub _construct_tree ($) { Line 3376  sub _construct_tree ($) {
3376              if ($token->{tag_name} eq 'table') {              if ($token->{tag_name} eq 'table') {
3377                ## have a table element in table scope                ## have a table element in table scope
3378                my $i;                my $i;
3379                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3380                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3381                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3382                    $i = $_;                    $i = $_;
3383                    last INSCOPE;                    last INSCOPE;
# Line 3279  sub _construct_tree ($) { Line 3388  sub _construct_tree ($) {
3388                  }                  }
3389                } # INSCOPE                } # INSCOPE
3390                unless (defined $i) {                unless (defined $i) {
3391                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3392                  ## Ignore the token                  ## Ignore the token
3393                  !!!next-token;                  !!!next-token;
3394                  redo B;                  redo B;
# Line 3289  sub _construct_tree ($) { Line 3398  sub _construct_tree ($) {
3398                if ({                if ({
3399                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3400                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3401                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3402                  !!!back-token;                  !!!back-token;
3403                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3404                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3405                  redo B;                  redo B;
3406                }                }
3407    
3408                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3409                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3410                }                }
3411    
3412                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3413    
3414                $reset_insertion_mode->();                $self->_reset_insertion_mode;
3415    
3416                !!!next-token;                !!!next-token;
3417                redo B;                redo B;
# Line 3311  sub _construct_tree ($) { Line 3420  sub _construct_tree ($) {
3420                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,
3421                        thead => 1, tr => 1,                        thead => 1, tr => 1,
3422                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3423                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3424                ## Ignore the token                ## Ignore the token
3425                !!!next-token;                !!!next-token;
3426                redo B;                redo B;
# Line 3322  sub _construct_tree ($) { Line 3431  sub _construct_tree ($) {
3431              #              #
3432            }            }
3433    
3434            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
3435            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
3436            redo B;            redo B;
3437          } elsif ($insertion_mode eq 'in caption') {          } elsif ($self->{insertion_mode} eq 'in caption') {
3438            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3439              ## NOTE: This is a code clone of "character in body".              ## NOTE: This is a code clone of "character in body".
3440              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
3441                            
3442              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3443    
3444              !!!next-token;              !!!next-token;
3445              redo B;              redo B;
3446            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3447              ## NOTE: This is a code clone of "comment in body".              ## NOTE: This is a code clone of "comment in body".
3448              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3449              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3450              !!!next-token;              !!!next-token;
3451              redo B;              redo B;
3452            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3345  sub _construct_tree ($) { Line 3454  sub _construct_tree ($) {
3454                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
3455                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
3456                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3457                !!!parse-error;                !!!parse-error (type => 'not closed:caption');
3458    
3459                ## As if </caption>                ## As if </caption>
3460                ## have a table element in table scope                ## have a table element in table scope
3461                my $i;                my $i;
3462                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3463                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3464                  if ($node->[1] eq 'caption') {                  if ($node->[1] eq 'caption') {
3465                    $i = $_;                    $i = $_;
3466                    last INSCOPE;                    last INSCOPE;
# Line 3362  sub _construct_tree ($) { Line 3471  sub _construct_tree ($) {
3471                  }                  }
3472                } # INSCOPE                } # INSCOPE
3473                unless (defined $i) {                unless (defined $i) {
3474                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:caption');
3475                  ## Ignore the token                  ## Ignore the token
3476                  !!!next-token;                  !!!next-token;
3477                  redo B;                  redo B;
# Line 3372  sub _construct_tree ($) { Line 3481  sub _construct_tree ($) {
3481                if ({                if ({
3482                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3483                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3484                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3485                  !!!back-token; # <?>                  !!!back-token; # <?>
3486                  $token = {type => 'end tag', tag_name => 'caption'};                  $token = {type => 'end tag', tag_name => 'caption'};
3487                  !!!back-token;                  !!!back-token;
3488                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3489                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3490                  redo B;                  redo B;
3491                }                }
3492    
3493                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3494                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3495                }                }
3496    
3497                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3498    
3499                $clear_up_to_marker->();                $clear_up_to_marker->();
3500    
3501                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3502    
3503                ## reprocess                ## reprocess
3504                redo B;                redo B;
# Line 3400  sub _construct_tree ($) { Line 3509  sub _construct_tree ($) {
3509              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'caption') {
3510                ## have a table element in table scope                ## have a table element in table scope
3511                my $i;                my $i;
3512                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3513                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3514                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3515                    $i = $_;                    $i = $_;
3516                    last INSCOPE;                    last INSCOPE;
# Line 3412  sub _construct_tree ($) { Line 3521  sub _construct_tree ($) {
3521                  }                  }
3522                } # INSCOPE                } # INSCOPE
3523                unless (defined $i) {                unless (defined $i) {
3524                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3525                  ## Ignore the token                  ## Ignore the token
3526                  !!!next-token;                  !!!next-token;
3527                  redo B;                  redo B;
# Line 3422  sub _construct_tree ($) { Line 3531  sub _construct_tree ($) {
3531                if ({                if ({
3532                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3533                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3534                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3535                  !!!back-token;                  !!!back-token;
3536                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3537                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3538                  redo B;                  redo B;
3539                }                }
3540    
3541                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3542                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3543                }                }
3544    
3545                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3546    
3547                $clear_up_to_marker->();                $clear_up_to_marker->();
3548    
3549                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3550    
3551                !!!next-token;                !!!next-token;
3552                redo B;                redo B;
3553              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3554                !!!parse-error;                !!!parse-error (type => 'not closed:caption');
3555    
3556                ## As if </caption>                ## As if </caption>
3557                ## have a table element in table scope                ## have a table element in table scope
3558                my $i;                my $i;
3559                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3560                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3561                  if ($node->[1] eq 'caption') {                  if ($node->[1] eq 'caption') {
3562                    $i = $_;                    $i = $_;
3563                    last INSCOPE;                    last INSCOPE;
# Line 3459  sub _construct_tree ($) { Line 3568  sub _construct_tree ($) {
3568                  }                  }
3569                } # INSCOPE                } # INSCOPE
3570                unless (defined $i) {                unless (defined $i) {
3571                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:caption');
3572                  ## Ignore the token                  ## Ignore the token
3573                  !!!next-token;                  !!!next-token;
3574                  redo B;                  redo B;
# Line 3469  sub _construct_tree ($) { Line 3578  sub _construct_tree ($) {
3578                if ({                if ({
3579                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3580                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3581                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3582                  !!!back-token; # </table>                  !!!back-token; # </table>
3583                  $token = {type => 'end tag', tag_name => 'caption'};                  $token = {type => 'end tag', tag_name => 'caption'};
3584                  !!!back-token;                  !!!back-token;
3585                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3586                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3587                  redo B;                  redo B;
3588                }                }
3589    
3590                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3591                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3592                }                }
3593    
3594                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3595    
3596                $clear_up_to_marker->();                $clear_up_to_marker->();
3597    
3598                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3599    
3600                ## reprocess                ## reprocess
3601                redo B;                redo B;
# Line 3495  sub _construct_tree ($) { Line 3604  sub _construct_tree ($) {
3604                        html => 1, tbody => 1, td => 1, tfoot => 1,                        html => 1, tbody => 1, td => 1, tfoot => 1,
3605                        th => 1, thead => 1, tr => 1,                        th => 1, thead => 1, tr => 1,
3606                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3607                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3608                ## Ignore the token                ## Ignore the token
3609                redo B;                redo B;
3610              } else {              } else {
# Line 3507  sub _construct_tree ($) { Line 3616  sub _construct_tree ($) {
3616                                
3617            $in_body->($insert_to_current);            $in_body->($insert_to_current);
3618            redo B;            redo B;
3619          } elsif ($insertion_mode eq 'in column group') {          } elsif ($self->{insertion_mode} eq 'in column group') {
3620            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3621              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3622                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3623                unless (length $token->{data}) {                unless (length $token->{data}) {
3624                  !!!next-token;                  !!!next-token;
3625                  redo B;                  redo B;
# Line 3520  sub _construct_tree ($) { Line 3629  sub _construct_tree ($) {
3629              #              #
3630            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3631              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3632              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3633              !!!next-token;              !!!next-token;
3634              redo B;              redo B;
3635            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
3636              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
3637                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3638                pop @$open_elements;                pop @{$self->{open_elements}};
3639                !!!next-token;                !!!next-token;
3640                redo B;                redo B;
3641              } else {              } else {
# Line 3534  sub _construct_tree ($) { Line 3643  sub _construct_tree ($) {
3643              }              }
3644            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
3645              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3646                if ($open_elements->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] eq 'html') {
3647                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:colgroup');
3648                  ## Ignore the token                  ## Ignore the token
3649                  !!!next-token;                  !!!next-token;
3650                  redo B;                  redo B;
3651                } else {                } else {
3652                  pop @$open_elements; # colgroup                  pop @{$self->{open_elements}}; # colgroup
3653                  $insertion_mode = 'in table';                  $self->{insertion_mode} = 'in table';
3654                  !!!next-token;                  !!!next-token;
3655                  redo B;                              redo B;            
3656                }                }
3657              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
3658                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:col');
3659                ## Ignore the token                ## Ignore the token
3660                !!!next-token;                !!!next-token;
3661                redo B;                redo B;
# Line 3558  sub _construct_tree ($) { Line 3667  sub _construct_tree ($) {
3667            }            }
3668    
3669            ## As if </colgroup>            ## As if </colgroup>
3670            if ($open_elements->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] eq 'html') {
3671              !!!parse-error;              !!!parse-error (type => 'unmatched end tag:colgroup');
3672              ## Ignore the token              ## Ignore the token
3673              !!!next-token;              !!!next-token;
3674              redo B;              redo B;
3675            } else {            } else {
3676              pop @$open_elements; # colgroup              pop @{$self->{open_elements}}; # colgroup
3677              $insertion_mode = 'in table';              $self->{insertion_mode} = 'in table';
3678              ## reprocess              ## reprocess
3679              redo B;              redo B;
3680            }            }
3681          } elsif ($insertion_mode eq 'in table body') {          } elsif ($self->{insertion_mode} eq 'in table body') {
3682            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3683              ## NOTE: This is a "character in table" code clone.              ## NOTE: This is a "character in table" code clone.
3684              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3685                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3686                                
3687                unless (length $token->{data}) {                unless (length $token->{data}) {
3688                  !!!next-token;                  !!!next-token;
# Line 3581  sub _construct_tree ($) { Line 3690  sub _construct_tree ($) {
3690                }                }
3691              }              }
3692    
3693                !!!parse-error (type => 'in table:#character');
3694    
3695              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3696              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3697              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3590  sub _construct_tree ($) { Line 3701  sub _construct_tree ($) {
3701              if ({              if ({
3702                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3703                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3704                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3705                # MUST                # MUST
3706                my $foster_parent_element;                my $foster_parent_element;
3707                my $next_sibling;                my $next_sibling;
3708                my $prev_sibling;                my $prev_sibling;
3709                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3710                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3711                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3712                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3713                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3714                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3715                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
3716                    } else {                    } else {
3717                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3718                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
3719                    }                    }
3720                    last OE;                    last OE;
3721                  }                  }
3722                } # OE                } # OE
3723                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
3724                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
3725                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
3726                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3621  sub _construct_tree ($) { Line 3732  sub _construct_tree ($) {
3732                     $next_sibling);                     $next_sibling);
3733                }                }
3734              } else {              } else {
3735                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3736              }              }
3737                            
3738              !!!next-token;              !!!next-token;
# Line 3629  sub _construct_tree ($) { Line 3740  sub _construct_tree ($) {
3740            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3741              ## Copied from 'in table'              ## Copied from 'in table'
3742              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3743              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3744              !!!next-token;              !!!next-token;
3745              redo B;              redo B;
3746            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3637  sub _construct_tree ($) { Line 3748  sub _construct_tree ($) {
3748                   tr => 1,                   tr => 1,
3749                   th => 1, td => 1,                   th => 1, td => 1,
3750                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3751                  unless ($token->{tag_name} eq 'tr') {
3752                    !!!parse-error (type => 'missing start tag:tr');
3753                  }
3754    
3755                ## Clear back to table body context                ## Clear back to table body context
3756                while (not {                while (not {
3757                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3758                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3759                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3760                  pop @$open_elements;                  pop @{$self->{open_elements}};
3761                }                }
3762                                
3763                $insertion_mode = 'in row';                $self->{insertion_mode} = 'in row';
3764                if ($token->{tag_name} eq 'tr') {                if ($token->{tag_name} eq 'tr') {
3765                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3766                  !!!next-token;                  !!!next-token;
# Line 3660  sub _construct_tree ($) { Line 3775  sub _construct_tree ($) {
3775                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3776                ## have an element in table scope                ## have an element in table scope
3777                my $i;                my $i;
3778                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3779                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3780                  if ({                  if ({
3781                       tbody => 1, thead => 1, tfoot => 1,                       tbody => 1, thead => 1, tfoot => 1,
3782                      }->{$node->[1]}) {                      }->{$node->[1]}) {
# Line 3674  sub _construct_tree ($) { Line 3789  sub _construct_tree ($) {
3789                  }                  }
3790                } # INSCOPE                } # INSCOPE
3791                unless (defined $i) {                unless (defined $i) {
3792                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3793                  ## Ignore the token                  ## Ignore the token
3794                  !!!next-token;                  !!!next-token;
3795                  redo B;                  redo B;
# Line 3683  sub _construct_tree ($) { Line 3798  sub _construct_tree ($) {
3798                ## Clear back to table body context                ## Clear back to table body context
3799                while (not {                while (not {
3800                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3801                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3802                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3803                  pop @$open_elements;                  pop @{$self->{open_elements}};
3804                }                }
3805    
3806                ## As if <{current node}>                ## As if <{current node}>
# Line 3695  sub _construct_tree ($) { Line 3810  sub _construct_tree ($) {
3810                ## Clear back to table body context                ## Clear back to table body context
3811                ## nop by definition                ## nop by definition
3812    
3813                pop @$open_elements;                pop @{$self->{open_elements}};
3814                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3815                ## reprocess                ## reprocess
3816                redo B;                redo B;
3817              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3818                ## NOTE: This is a code clone of "table in table"                ## NOTE: This is a code clone of "table in table"
3819                !!!parse-error;                !!!parse-error (type => 'not closed:table');
3820    
3821                ## As if </table>                ## As if </table>
3822                ## have a table element in table scope                ## have a table element in table scope
3823                my $i;                my $i;
3824                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3825                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3826                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
3827                    $i = $_;                    $i = $_;
3828                    last INSCOPE;                    last INSCOPE;
# Line 3718  sub _construct_tree ($) { Line 3833  sub _construct_tree ($) {
3833                  }                  }
3834                } # INSCOPE                } # INSCOPE
3835                unless (defined $i) {                unless (defined $i) {
3836                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
3837                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
3838                  !!!next-token;                  !!!next-token;
3839                  redo B;                  redo B;
# Line 3728  sub _construct_tree ($) { Line 3843  sub _construct_tree ($) {
3843                if ({                if ({
3844                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3845                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3846                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3847                  !!!back-token; # <table>                  !!!back-token; # <table>
3848                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
3849                  !!!back-token;                  !!!back-token;
3850                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3851                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3852                  redo B;                  redo B;
3853                }                }
3854    
3855                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3856                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3857                }                }
3858    
3859                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3860    
3861                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
3862    
3863                ## reprocess                ## reprocess
3864                redo B;                redo B;
# Line 3756  sub _construct_tree ($) { Line 3871  sub _construct_tree ($) {
3871                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3872                ## have an element in table scope                ## have an element in table scope
3873                my $i;                my $i;
3874                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3875                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3876                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3877                    $i = $_;                    $i = $_;
3878                    last INSCOPE;                    last INSCOPE;
# Line 3768  sub _construct_tree ($) { Line 3883  sub _construct_tree ($) {
3883                  }                  }
3884                } # INSCOPE                } # INSCOPE
3885                unless (defined $i) {                unless (defined $i) {
3886                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3887                  ## Ignore the token                  ## Ignore the token
3888                  !!!next-token;                  !!!next-token;
3889                  redo B;                  redo B;
# Line 3777  sub _construct_tree ($) { Line 3892  sub _construct_tree ($) {
3892                ## Clear back to table body context                ## Clear back to table body context
3893                while (not {                while (not {
3894                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3895                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3896                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3897                  pop @$open_elements;                  pop @{$self->{open_elements}};
3898                }                }
3899    
3900                pop @$open_elements;                pop @{$self->{open_elements}};
3901                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3902                !!!next-token;                !!!next-token;
3903                redo B;                redo B;
3904              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3905                ## have an element in table scope                ## have an element in table scope
3906                my $i;                my $i;
3907                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3908                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3909                  if ({                  if ({
3910                       tbody => 1, thead => 1, tfoot => 1,                       tbody => 1, thead => 1, tfoot => 1,
3911                      }->{$node->[1]}) {                      }->{$node->[1]}) {
# Line 3803  sub _construct_tree ($) { Line 3918  sub _construct_tree ($) {
3918                  }                  }
3919                } # INSCOPE                } # INSCOPE
3920                unless (defined $i) {                unless (defined $i) {
3921                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3922                  ## Ignore the token                  ## Ignore the token
3923                  !!!next-token;                  !!!next-token;
3924                  redo B;                  redo B;
# Line 3812  sub _construct_tree ($) { Line 3927  sub _construct_tree ($) {
3927                ## Clear back to table body context                ## Clear back to table body context
3928                while (not {                while (not {
3929                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3930                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3931                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3932                  pop @$open_elements;                  pop @{$self->{open_elements}};
3933                }                }
3934    
3935                ## As if <{current node}>                ## As if <{current node}>
# Line 3824  sub _construct_tree ($) { Line 3939  sub _construct_tree ($) {
3939                ## Clear back to table body context                ## Clear back to table body context
3940                ## nop by definition                ## nop by definition
3941    
3942                pop @$open_elements;                pop @{$self->{open_elements}};
3943                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3944                ## reprocess                ## reprocess
3945                redo B;                redo B;
3946              } elsif ({              } elsif ({
3947                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
3948                        html => 1, td => 1, th => 1, tr => 1,                        html => 1, td => 1, th => 1, tr => 1,
3949                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3950                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3951                ## Ignore the token                ## Ignore the token
3952                !!!next-token;                !!!next-token;
3953                redo B;                redo B;
# Line 3844  sub _construct_tree ($) { Line 3959  sub _construct_tree ($) {
3959            }            }
3960                        
3961            ## As if in table            ## As if in table
3962            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
3963            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
3964            redo B;            redo B;
3965          } elsif ($insertion_mode eq 'in row') {          } elsif ($self->{insertion_mode} eq 'in row') {
3966            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3967              ## NOTE: This is a "character in table" code clone.              ## NOTE: This is a "character in table" code clone.
3968              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3969                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3970                                
3971                unless (length $token->{data}) {                unless (length $token->{data}) {
3972                  !!!next-token;                  !!!next-token;
# Line 3859  sub _construct_tree ($) { Line 3974  sub _construct_tree ($) {
3974                }                }
3975              }              }
3976    
3977                !!!parse-error (type => 'in table:#character');
3978    
3979              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3980              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3981              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3868  sub _construct_tree ($) { Line 3985  sub _construct_tree ($) {
3985              if ({              if ({
3986                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3987                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3988                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3989                # MUST                # MUST
3990                my $foster_parent_element;                my $foster_parent_element;
3991                my $next_sibling;                my $next_sibling;
3992                my $prev_sibling;                my $prev_sibling;
3993                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3994                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3995                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3996                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3997                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3998                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3999                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
4000                    } else {                    } else {
4001                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
4002                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
4003                    }                    }
4004                    last OE;                    last OE;
4005                  }                  }
4006                } # OE                } # OE
4007                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
4008                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
4009                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
4010                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3899  sub _construct_tree ($) { Line 4016  sub _construct_tree ($) {
4016                     $next_sibling);                     $next_sibling);
4017                }                }
4018              } else {              } else {
4019                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4020              }              }
4021                            
4022              !!!next-token;              !!!next-token;
# Line 3907  sub _construct_tree ($) { Line 4024  sub _construct_tree ($) {
4024            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4025              ## Copied from 'in table'              ## Copied from 'in table'
4026              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4027              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4028              !!!next-token;              !!!next-token;
4029              redo B;              redo B;
4030            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3916  sub _construct_tree ($) { Line 4033  sub _construct_tree ($) {
4033                ## Clear back to table row context                ## Clear back to table row context
4034                while (not {                while (not {
4035                  tr => 1, html => 1,                  tr => 1, html => 1,
4036                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4037                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4038                  pop @$open_elements;                  pop @{$self->{open_elements}};
4039                }                }
4040                                
4041                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4042                $insertion_mode = 'in cell';                $self->{insertion_mode} = 'in cell';
4043    
4044                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
4045                                
# Line 3935  sub _construct_tree ($) { Line 4052  sub _construct_tree ($) {
4052                ## As if </tr>                ## As if </tr>
4053                ## have an element in table scope                ## have an element in table scope
4054                my $i;                my $i;
4055                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4056                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4057                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4058                    $i = $_;                    $i = $_;
4059                    last INSCOPE;                    last INSCOPE;
# Line 3947  sub _construct_tree ($) { Line 4064  sub _construct_tree ($) {
4064                  }                  }
4065                } # INSCOPE                } # INSCOPE
4066                unless (defined $i) {                unless (defined $i) {
4067                  !!!parse-error;                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});
4068                  ## Ignore the token                  ## Ignore the token
4069                  !!!next-token;                  !!!next-token;
4070                  redo B;                  redo B;
# Line 3956  sub _construct_tree ($) { Line 4073  sub _construct_tree ($) {
4073                ## Clear back to table row context                ## Clear back to table row context
4074                while (not {                while (not {
4075                  tr => 1, html => 1,                  tr => 1, html => 1,
4076                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4077                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4078                  pop @$open_elements;                  pop @{$self->{open_elements}};
4079                }                }
4080    
4081                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4082                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4083                ## reprocess                ## reprocess
4084                redo B;                redo B;
4085              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4086                ## NOTE: This is a code clone of "table in table"                ## NOTE: This is a code clone of "table in table"
4087                !!!parse-error;                !!!parse-error (type => 'not closed:table');
4088    
4089                ## As if </table>                ## As if </table>
4090                ## have a table element in table scope                ## have a table element in table scope
4091                my $i;                my $i;
4092                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4093                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4094                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
4095                    $i = $_;                    $i = $_;
4096                    last INSCOPE;                    last INSCOPE;
# Line 3984  sub _construct_tree ($) { Line 4101  sub _construct_tree ($) {
4101                  }                  }
4102                } # INSCOPE                } # INSCOPE
4103                unless (defined $i) {                unless (defined $i) {
4104                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
4105                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
4106                  !!!next-token;                  !!!next-token;
4107                  redo B;                  redo B;
# Line 3994  sub _construct_tree ($) { Line 4111  sub _construct_tree ($) {
4111                if ({                if ({
4112                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
4113                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
4114                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
4115                  !!!back-token; # <table>                  !!!back-token; # <table>
4116                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
4117                  !!!back-token;                  !!!back-token;
4118                  $token = {type => 'end tag',                  $token = {type => 'end tag',
4119                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4120                  redo B;                  redo B;
4121                }                }
4122    
4123                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
4124                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4125                }                }
4126    
4127                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4128    
4129                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
4130    
4131                ## reprocess                ## reprocess
4132                redo B;                redo B;
# Line 4020  sub _construct_tree ($) { Line 4137  sub _construct_tree ($) {
4137              if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
4138                ## have an element in table scope                ## have an element in table scope
4139                my $i;                my $i;
4140                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4141                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4142                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4143                    $i = $_;                    $i = $_;
4144                    last INSCOPE;                    last INSCOPE;
# Line 4032  sub _construct_tree ($) { Line 4149  sub _construct_tree ($) {
4149                  }                  }
4150                } # INSCOPE                } # INSCOPE
4151                unless (defined $i) {                unless (defined $i) {
4152                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4153                  ## Ignore the token                  ## Ignore the token
4154                  !!!next-token;                  !!!next-token;
4155                  redo B;                  redo B;
# Line 4041  sub _construct_tree ($) { Line 4158  sub _construct_tree ($) {
4158                ## Clear back to table row context                ## Clear back to table row context
4159                while (not {                while (not {
4160                  tr => 1, html => 1,                  tr => 1, html => 1,
4161                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4162                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4163                  pop @$open_elements;                  pop @{$self->{open_elements}};
4164                }                }
4165    
4166                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4167                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4168                !!!next-token;                !!!next-token;
4169                redo B;                redo B;
4170              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4171                ## As if </tr>                ## As if </tr>
4172                ## have an element in table scope                ## have an element in table scope
4173                my $i;                my $i;
4174                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4175                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4176                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4177                    $i = $_;                    $i = $_;
4178                    last INSCOPE;                    last INSCOPE;
# Line 4066  sub _construct_tree ($) { Line 4183  sub _construct_tree ($) {
4183                  }                  }
4184                } # INSCOPE                } # INSCOPE
4185                unless (defined $i) {                unless (defined $i) {
4186                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});
4187                  ## Ignore the token                  ## Ignore the token
4188                  !!!next-token;                  !!!next-token;
4189                  redo B;                  redo B;
# Line 4075  sub _construct_tree ($) { Line 4192  sub _construct_tree ($) {
4192                ## Clear back to table row context                ## Clear back to table row context
4193                while (not {                while (not {
4194                  tr => 1, html => 1,                  tr => 1, html => 1,
4195                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4196                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4197                  pop @$open_elements;                  pop @{$self->{open_elements}};
4198                }                }
4199    
4200                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4201                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4202                ## reprocess                ## reprocess
4203                redo B;                redo B;
4204              } elsif ({              } elsif ({
# Line 4089  sub _construct_tree ($) { Line 4206  sub _construct_tree ($) {
4206                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4207                ## have an element in table scope                ## have an element in table scope
4208                my $i;                my $i;
4209                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4210                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4211                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4212                    $i = $_;                    $i = $_;
4213                    last INSCOPE;                    last INSCOPE;
# Line 4101  sub _construct_tree ($) { Line 4218  sub _construct_tree ($) {
4218                  }                  }
4219                } # INSCOPE                } # INSCOPE
4220                unless (defined $i) {                unless (defined $i) {
4221                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4222                  ## Ignore the token                  ## Ignore the token
4223                  !!!next-token;                  !!!next-token;
4224                  redo B;                  redo B;
# Line 4110  sub _construct_tree ($) { Line 4227  sub _construct_tree ($) {
4227                ## As if </tr>                ## As if </tr>
4228                ## have an element in table scope                ## have an element in table scope
4229                my $i;                my $i;
4230                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4231                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4232                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4233                    $i = $_;                    $i = $_;
4234                    last INSCOPE;                    last INSCOPE;
# Line 4122  sub _construct_tree ($) { Line 4239  sub _construct_tree ($) {
4239                  }                  }
4240                } # INSCOPE                } # INSCOPE
4241                unless (defined $i) {                unless (defined $i) {
4242                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:tr');
4243                  ## Ignore the token                  ## Ignore the token
4244                  !!!next-token;                  !!!next-token;
4245                  redo B;                  redo B;
# Line 4131  sub _construct_tree ($) { Line 4248  sub _construct_tree ($) {
4248                ## Clear back to table row context                ## Clear back to table row context
4249                while (not {                while (not {
4250                  tr => 1, html => 1,                  tr => 1, html => 1,
4251                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4252                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4253                  pop @$open_elements;                  pop @{$self->{open_elements}};
4254                }                }
4255    
4256                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4257                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4258                ## reprocess                ## reprocess
4259                redo B;                redo B;
4260              } elsif ({              } elsif ({
4261                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1,
4262                        colgroup => 1, html => 1, td => 1, th => 1,                        colgroup => 1, html => 1, td => 1, th => 1,
4263                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4264                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4265                ## Ignore the token                ## Ignore the token
4266                !!!next-token;                !!!next-token;
4267                redo B;                redo B;
# Line 4156  sub _construct_tree ($) { Line 4273  sub _construct_tree ($) {
4273            }            }
4274    
4275            ## As if in table            ## As if in table
4276            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
4277            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
4278            redo B;            redo B;
4279          } elsif ($insertion_mode eq 'in cell') {          } elsif ($self->{insertion_mode} eq 'in cell') {
4280            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4281              ## NOTE: This is a code clone of "character in body".              ## NOTE: This is a code clone of "character in body".
4282              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
4283                            
4284              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4285    
4286              !!!next-token;              !!!next-token;
4287              redo B;              redo B;
4288            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4289              ## NOTE: This is a code clone of "comment in body".              ## NOTE: This is a code clone of "comment in body".
4290              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4291              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4292              !!!next-token;              !!!next-token;
4293              redo B;              redo B;
4294            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4182  sub _construct_tree ($) { Line 4299  sub _construct_tree ($) {
4299                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4300                ## have an element in table scope                ## have an element in table scope
4301                my $tn;                my $tn;
4302                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4303                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4304                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {
4305                    $tn = $node->[1];                    $tn = $node->[1];
4306                    last INSCOPE;                    last INSCOPE;
# Line 4194  sub _construct_tree ($) { Line 4311  sub _construct_tree ($) {
4311                  }                  }
4312                } # INSCOPE                } # INSCOPE
4313                unless (defined $tn) {                unless (defined $tn) {
4314                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4315                  ## Ignore the token                  ## Ignore the token
4316                  !!!next-token;                  !!!next-token;
4317                  redo B;                  redo B;
# Line 4211  sub _construct_tree ($) { Line 4328  sub _construct_tree ($) {
4328              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
4329                ## have an element in table scope                ## have an element in table scope
4330                my $i;                my $i;
4331                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4332                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4333                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4334                    $i = $_;                    $i = $_;
4335                    last INSCOPE;                    last INSCOPE;
# Line 4223  sub _construct_tree ($) { Line 4340  sub _construct_tree ($) {
4340                  }                  }
4341                } # INSCOPE                } # INSCOPE
4342                unless (defined $i) {                unless (defined $i) {
4343                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4344                  ## Ignore the token                  ## Ignore the token
4345                  !!!next-token;                  !!!next-token;
4346                  redo B;                  redo B;
# Line 4235  sub _construct_tree ($) { Line 4352  sub _construct_tree ($) {
4352                     td => ($token->{tag_name} eq 'th'),                     td => ($token->{tag_name} eq 'th'),
4353                     th => ($token->{tag_name} eq 'td'),                     th => ($token->{tag_name} eq 'td'),
4354                     tr => 1,                     tr => 1,
4355                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
4356                  !!!back-token;                  !!!back-token;
4357                  $token = {type => 'end tag',                  $token = {type => 'end tag',
4358                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4359                  redo B;                  redo B;
4360                }                }
4361    
4362                if ($open_elements->[-1]->[1] ne $token->{tag_name}) {                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
4363                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4364                }                }
4365    
4366                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4367    
4368                $clear_up_to_marker->();                $clear_up_to_marker->();
4369    
4370                $insertion_mode = 'in row';                $self->{insertion_mode} = 'in row';
4371    
4372                !!!next-token;                !!!next-token;
4373                redo B;                redo B;
# Line 4258  sub _construct_tree ($) { Line 4375  sub _construct_tree ($) {
4375                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1,
4376                        colgroup => 1, html => 1,                        colgroup => 1, html => 1,
4377                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4378                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4379                ## Ignore the token                ## Ignore the token
4380                !!!next-token;                !!!next-token;
4381                redo B;                redo B;
# Line 4269  sub _construct_tree ($) { Line 4386  sub _construct_tree ($) {
4386                ## have an element in table scope                ## have an element in table scope
4387                my $i;                my $i;
4388                my $tn;                my $tn;
4389                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4390                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4391                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4392                    $i = $_;                    $i = $_;
4393                    last INSCOPE;                    last INSCOPE;
# Line 4285  sub _construct_tree ($) { Line 4402  sub _construct_tree ($) {
4402                  }                  }
4403                } # INSCOPE                } # INSCOPE
4404                unless (defined $i) {                unless (defined $i) {
4405                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4406                  ## Ignore the token                  ## Ignore the token
4407                  !!!next-token;                  !!!next-token;
4408                  redo B;                  redo B;
# Line 4304  sub _construct_tree ($) { Line 4421  sub _construct_tree ($) {
4421                        
4422            $in_body->($insert_to_current);            $in_body->($insert_to_current);
4423            redo B;            redo B;
4424          } elsif ($insertion_mode eq 'in select') {          } elsif ($self->{insertion_mode} eq 'in select') {
4425            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4426              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4427              !!!next-token;              !!!next-token;
4428              redo B;              redo B;
4429            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4430              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4431              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4432              !!!next-token;              !!!next-token;
4433              redo B;              redo B;
4434            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
4435              if ($token->{tag_name} eq 'option') {              if ($token->{tag_name} eq 'option') {
4436                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4437                  ## As if </option>                  ## As if </option>
4438                  pop @$open_elements;                  pop @{$self->{open_elements}};
4439                }                }
4440    
4441                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4442                !!!next-token;                !!!next-token;
4443                redo B;                redo B;
4444              } elsif ($token->{tag_name} eq 'optgroup') {              } elsif ($token->{tag_name} eq 'optgroup') {
4445                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4446                  ## As if </option>                  ## As if </option>
4447                  pop @$open_elements;                  pop @{$self->{open_elements}};
4448                }                }
4449    
4450                if ($open_elements->[-1]->[1] eq 'optgroup') {                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
4451                  ## As if </optgroup>                  ## As if </optgroup>
4452                  pop @$open_elements;                  pop @{$self->{open_elements}};
4453                }                }
4454    
4455                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4456                !!!next-token;                !!!next-token;
4457                redo B;                redo B;
4458              } elsif ($token->{tag_name} eq 'select') {              } elsif ($token->{tag_name} eq 'select') {
4459                !!!parse-error;                !!!parse-error (type => 'not closed:select');
4460                ## As if </select> instead                ## As if </select> instead
4461                ## have an element in table scope                ## have an element in table scope
4462                my $i;                my $i;
4463                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4464                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4465                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4466                    $i = $_;                    $i = $_;
4467                    last INSCOPE;                    last INSCOPE;
# Line 4355  sub _construct_tree ($) { Line 4472  sub _construct_tree ($) {
4472                  }                  }
4473                } # INSCOPE                } # INSCOPE
4474                unless (defined $i) {                unless (defined $i) {
4475                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:select');
4476                  ## Ignore the token                  ## Ignore the token
4477                  !!!next-token;                  !!!next-token;
4478                  redo B;                  redo B;
4479                }                }
4480                                
4481                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4482    
4483                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4484    
4485                !!!next-token;                !!!next-token;
4486                redo B;                redo B;
# Line 4372  sub _construct_tree ($) { Line 4489  sub _construct_tree ($) {
4489              }              }
4490            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4491              if ($token->{tag_name} eq 'optgroup') {              if ($token->{tag_name} eq 'optgroup') {
4492                if ($open_elements->[-1]->[1] eq 'option' and                if ($self->{open_elements}->[-1]->[1] eq 'option' and
4493                    $open_elements->[-2]->[1] eq 'optgroup') {                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {
4494                  ## As if </option>                  ## As if </option>
4495                  splice @$open_elements, -2;                  splice @{$self->{open_elements}}, -2;
4496                } elsif ($open_elements->[-1]->[1] eq 'optgroup') {                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
4497                  pop @$open_elements;                  pop @{$self->{open_elements}};
4498                } else {                } else {
4499                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4500                  ## Ignore the token                  ## Ignore the token
4501                }                }
4502                !!!next-token;                !!!next-token;
4503                redo B;                redo B;
4504              } elsif ($token->{tag_name} eq 'option') {              } elsif ($token->{tag_name} eq 'option') {
4505                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4506                  pop @$open_elements;                  pop @{$self->{open_elements}};
4507                } else {                } else {
4508                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4509                  ## Ignore the token                  ## Ignore the token
4510                }                }
4511                !!!next-token;                !!!next-token;
# Line 4396  sub _construct_tree ($) { Line 4513  sub _construct_tree ($) {
4513              } elsif ($token->{tag_name} eq 'select') {              } elsif ($token->{tag_name} eq 'select') {
4514                ## have an element in table scope                ## have an element in table scope
4515                my $i;                my $i;
4516                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4517                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4518                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4519                    $i = $_;                    $i = $_;
4520                    last INSCOPE;                    last INSCOPE;
# Line 4408  sub _construct_tree ($) { Line 4525  sub _construct_tree ($) {
4525                  }                  }
4526                } # INSCOPE                } # INSCOPE
4527                unless (defined $i) {                unless (defined $i) {
4528                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4529                  ## Ignore the token                  ## Ignore the token
4530                  !!!next-token;                  !!!next-token;
4531                  redo B;                  redo B;
4532                }                }
4533                                
4534                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4535    
4536                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4537    
4538                !!!next-token;                !!!next-token;
4539                redo B;                redo B;
# Line 4424  sub _construct_tree ($) { Line 4541  sub _construct_tree ($) {
4541                        caption => 1, table => 1, tbody => 1,                        caption => 1, table => 1, tbody => 1,
4542                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
4543                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4544                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4545                                
4546                ## have an element in table scope                ## have an element in table scope
4547                my $i;                my $i;
4548                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4549                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4550                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4551                    $i = $_;                    $i = $_;
4552                    last INSCOPE;                    last INSCOPE;
# Line 4448  sub _construct_tree ($) { Line 4565  sub _construct_tree ($) {
4565                ## As if </select>                ## As if </select>
4566                ## have an element in table scope                ## have an element in table scope
4567                undef $i;                undef $i;
4568                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4569                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4570                  if ($node->[1] eq 'select') {                  if ($node->[1] eq 'select') {
4571                    $i = $_;                    $i = $_;
4572                    last INSCOPE;                    last INSCOPE;
# Line 4460  sub _construct_tree ($) { Line 4577  sub _construct_tree ($) {
4577                  }                  }
4578                } # INSCOPE                } # INSCOPE
4579                unless (defined $i) {                unless (defined $i) {
4580                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:select');
4581                  ## Ignore the </select> token                  ## Ignore the </select> token
4582                  !!!next-token; ## TODO: ok?                  !!!next-token; ## TODO: ok?
4583                  redo B;                  redo B;
4584                }                }
4585                                
4586                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4587    
4588                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4589    
4590                ## reprocess                ## reprocess
4591                redo B;                redo B;
# Line 4479  sub _construct_tree ($) { Line 4596  sub _construct_tree ($) {
4596              #              #
4597            }            }
4598    
4599            !!!parse-error;            !!!parse-error (type => 'in select:'.$token->{tag_name});
4600            ## Ignore the token            ## Ignore the token
4601            !!!next-token;            !!!next-token;
4602            redo B;            redo B;
4603          } elsif ($insertion_mode eq 'after body') {          } elsif ($self->{insertion_mode} eq 'after body') {
4604            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4605              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4606                ## As if in body                ## As if in body
4607                $reconstruct_active_formatting_elements->($insert_to_current);                $reconstruct_active_formatting_elements->($insert_to_current);
4608                                
4609                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4610    
4611                unless (length $token->{data}) {                unless (length $token->{data}) {
4612                  !!!next-token;                  !!!next-token;
# Line 4498  sub _construct_tree ($) { Line 4615  sub _construct_tree ($) {
4615              }              }
4616                            
4617              #              #
4618                !!!parse-error (type => 'after body:#'.$token->{type});
4619            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4620              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4621              $open_elements->[0]->[0]->append_child ($comment);              $self->{open_elements}->[0]->[0]->append_child ($comment);
4622              !!!next-token;              !!!next-token;
4623              redo B;              redo B;
4624              } elsif ($token->{type} eq 'start tag') {
4625                !!!parse-error (type => 'after body:'.$token->{tag_name});
4626                #
4627            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4628              if ($token->{tag_name} eq 'html') {              if ($token->{tag_name} eq 'html') {
4629                ## TODO: if inner_html, parse-error, ignore the token; otherwise,                if (defined $self->{inner_html_node}) {
4630                    !!!parse-error (type => 'unmatched end tag:html');
4631                $phase = 'trailing end';                  ## Ignore the token
4632                !!!next-token;                  !!!next-token;
4633                redo B;                  redo B;
4634                  } else {
4635                    $phase = 'trailing end';
4636                    !!!next-token;
4637                    redo B;
4638                  }
4639              } else {              } else {
4640                #                !!!parse-error (type => 'after body:/'.$token->{tag_name});
4641              }              }
4642            } else {            } else {
4643              #              !!!parse-error (type => 'after body:#'.$token->{type});
4644            }            }
4645    
4646            !!!parse-error ('data after body');            $self->{insertion_mode} = 'in body';
           $insertion_mode = 'in body';  
4647            ## reprocess            ## reprocess
4648            redo B;            redo B;
4649          } elsif ($insertion_mode eq 'in frameset') {          } elsif ($self->{insertion_mode} eq 'in frameset') {
4650            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4651              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4652                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4653    
4654                unless (length $token->{data}) {                unless (length $token->{data}) {
4655                  !!!next-token;                  !!!next-token;
# Line 4535  sub _construct_tree ($) { Line 4660  sub _construct_tree ($) {
4660              #              #
4661            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4662              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4663              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4664              !!!next-token;              !!!next-token;
4665              redo B;              redo B;
4666            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4545  sub _construct_tree ($) { Line 4670  sub _construct_tree ($) {
4670                redo B;                redo B;
4671              } elsif ($token->{tag_name} eq 'frame') {              } elsif ($token->{tag_name} eq 'frame') {
4672                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4673                pop @$open_elements;                pop @{$self->{open_elements}};
4674                !!!next-token;                !!!next-token;
4675                redo B;                redo B;
4676              } elsif ($token->{tag_name} eq 'noframes') {              } elsif ($token->{tag_name} eq 'noframes') {
# Line 4556  sub _construct_tree ($) { Line 4681  sub _construct_tree ($) {
4681              }              }
4682            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4683              if ($token->{tag_name} eq 'frameset') {              if ($token->{tag_name} eq 'frameset') {
4684                if ($open_elements->[-1]->[1] eq 'html' and                if ($self->{open_elements}->[-1]->[1] eq 'html' and
4685                    @$open_elements == 1) {                    @{$self->{open_elements}} == 1) {
4686                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4687                  ## Ignore the token                  ## Ignore the token
4688                  !!!next-token;                  !!!next-token;
4689                } else {                } else {
4690                  pop @$open_elements;                  pop @{$self->{open_elements}};
4691                  !!!next-token;                  !!!next-token;
4692                }                }
4693                                
4694                ## if not inner_html and                ## if not inner_html and
4695                if ($open_elements->[-1]->[1] ne 'frameset') {                if ($self->{open_elements}->[-1]->[1] ne 'frameset') {
4696                  $insertion_mode = 'after frameset';                  $self->{insertion_mode} = 'after frameset';
4697                }                }
4698                redo B;                redo B;
4699              } else {              } else {
# Line 4578  sub _construct_tree ($) { Line 4703  sub _construct_tree ($) {
4703              #              #
4704            }            }
4705                        
4706            !!!parse-error;            if (defined $token->{tag_name}) {
4707                !!!parse-error (type => 'in frameset:'.$token->{tag_name});
4708              } else {
4709                !!!parse-error (type => 'in frameset:#'.$token->{type});
4710              }
4711            ## Ignore the token            ## Ignore the token
4712            !!!next-token;            !!!next-token;
4713            redo B;            redo B;
4714          } elsif ($insertion_mode eq 'after frameset') {          } elsif ($self->{insertion_mode} eq 'after frameset') {
4715            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4716              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4717                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4718    
4719                unless (length $token->{data}) {                unless (length $token->{data}) {
4720                  !!!next-token;                  !!!next-token;
# Line 4596  sub _construct_tree ($) { Line 4725  sub _construct_tree ($) {
4725              #              #
4726            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4727              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4728              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4729              !!!next-token;              !!!next-token;
4730              redo B;              redo B;
4731            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4618  sub _construct_tree ($) { Line 4747  sub _construct_tree ($) {
4747              #              #
4748            }            }
4749                        
4750            !!!parse-error;            if (defined $token->{tag_name}) {
4751                !!!parse-error (type => 'after frameset:'.$token->{tag_name});
4752              } else {
4753                !!!parse-error (type => 'after frameset:#'.$token->{type});
4754              }
4755            ## Ignore the token            ## Ignore the token
4756            !!!next-token;            !!!next-token;
4757            redo B;            redo B;
4758    
4759            ## ISSUE: An issue in spec there            ## ISSUE: An issue in spec there
4760          } else {          } else {
4761            die "$0: $insertion_mode: Unknown insertion mode";            die "$0: $self->{insertion_mode}: Unknown insertion mode";
4762          }          }
4763        }        }
4764      } elsif ($phase eq 'trailing end') {      } elsif ($phase eq 'trailing end') {
4765        ## states in the main stage is preserved yet # MUST        ## states in the main stage is preserved yet # MUST
4766                
4767        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} eq 'DOCTYPE') {
4768          !!!parse-error;          !!!parse-error (type => 'after html:#DOCTYPE');
4769          ## Ignore the token          ## Ignore the token
4770          !!!next-token;          !!!next-token;
4771          redo B;          redo B;
# Line 4651  sub _construct_tree ($) { Line 4784  sub _construct_tree ($) {
4784            $reconstruct_active_formatting_elements->($insert_to_current)            $reconstruct_active_formatting_elements->($insert_to_current)
4785              if $phase eq 'main';              if $phase eq 'main';
4786                        
4787            $open_elements->[-1]->[0]->manakai_append_text ($data);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);
4788                        
4789            unless (length $token->{data}) {            unless (length $token->{data}) {
4790              !!!next-token;              !!!next-token;
# Line 4659  sub _construct_tree ($) { Line 4792  sub _construct_tree ($) {
4792            }            }
4793          }          }
4794    
4795          !!!parse-error;          !!!parse-error (type => 'after html:#character');
4796          $phase = 'main';          $phase = 'main';
4797          ## reprocess          ## reprocess
4798          redo B;          redo B;
4799        } elsif ($token->{type} eq 'start tag' or        } elsif ($token->{type} eq 'start tag' or
4800                 $token->{type} eq 'end tag') {                 $token->{type} eq 'end tag') {
4801          !!!parse-error;          !!!parse-error (type => 'after html:'.$token->{tag_name});
4802          $phase = 'main';          $phase = 'main';
4803          ## reprocess          ## reprocess
4804          redo B;          redo B;
# Line 4681  sub _construct_tree ($) { Line 4814  sub _construct_tree ($) {
4814    ## Stop parsing # MUST    ## Stop parsing # MUST
4815        
4816    ## TODO: script stuffs    ## TODO: script stuffs
4817  } # _construct_tree  } # _tree_construct_main
4818    
4819    sub set_inner_html ($$$) {
4820      my $class = shift;
4821      my $node = shift;
4822      my $s = \$_[0];
4823      my $onerror = $_[1];
4824    
4825      my $nt = $node->node_type;
4826      if ($nt == 9) {
4827        # MUST
4828        
4829        ## Step 1 # MUST
4830        ## TODO: If the document has an active parser, ...
4831        ## ISSUE: There is an issue in the spec.
4832        
4833        ## Step 2 # MUST
4834        my @cn = @{$node->child_nodes};
4835        for (@cn) {
4836          $node->remove_child ($_);
4837        }
4838    
4839        ## Step 3, 4, 5 # MUST
4840        $class->parse_string ($$s => $node, $onerror);
4841      } elsif ($nt == 1) {
4842        ## TODO: If non-html element
4843    
4844        ## NOTE: Most of this code is copied from |parse_string|
4845    
4846        ## Step 1 # MUST
4847        my $doc = $node->owner_document->implementation->create_document;
4848        ## TODO: Mark as HTML document
4849        my $p = $class->new;
4850        $p->{document} = $doc;
4851    
4852        ## Step 9 # MUST
4853        my $i = 0;
4854        my $line = 1;
4855        my $column = 0;
4856        $p->{set_next_input_character} = sub {
4857          my $self = shift;
4858          $self->{next_input_character} = -1 and return if $i >= length $$s;
4859          $self->{next_input_character} = ord substr $$s, $i++, 1;
4860          $column++;
4861    
4862          if ($self->{next_input_character} == 0x000A) { # LF
4863            $line++;
4864            $column = 0;
4865          } elsif ($self->{next_input_character} == 0x000D) { # CR
4866            if ($i >= length $$s) {
4867              #
4868            } else {
4869              my $next_char = ord substr $$s, $i++, 1;
4870              if ($next_char == 0x000A) { # LF
4871                #
4872              } else {
4873                push @{$self->{char}}, $next_char;
4874              }
4875            }
4876            $self->{next_input_character} = 0x000A; # LF # MUST
4877            $line++;
4878            $column = 0;
4879          } elsif ($self->{next_input_character} > 0x10FFFF) {
4880            $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
4881          } elsif ($self->{next_input_character} == 0x0000) { # NULL
4882            $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
4883          }
4884        };
4885        
4886        my $ponerror = $onerror || sub {
4887          my (%opt) = @_;
4888          warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";
4889        };
4890        $p->{parse_error} = sub {
4891          $ponerror->(@_, line => $line, column => $column);
4892        };
4893        
4894        $p->_initialize_tokenizer;
4895        $p->_initialize_tree_constructor;
4896    
4897        ## Step 2
4898        my $node_ln = $node->local_name;
4899        $p->{content_model_flag} = {
4900          title => 'RCDATA',
4901          textarea => 'RCDATA',
4902          style => 'CDATA',
4903          script => 'CDATA',
4904          xmp => 'CDATA',
4905          iframe => 'CDATA',
4906          noembed => 'CDATA',
4907          noframes => 'CDATA',
4908          noscript => 'CDATA',
4909          plaintext => 'PLAINTEXT',
4910        }->{$node_ln} || 'PCDATA';
4911           ## ISSUE: What is "the name of the element"? local name?
4912    
4913        $p->{inner_html_node} = [$node, $node_ln];
4914    
4915        ## Step 4
4916        my $root = $doc->create_element_ns
4917          ('http://www.w3.org/1999/xhtml', [undef, 'html']);
4918    
4919        ## Step 5 # MUST
4920        $doc->append_child ($root);
4921    
4922        ## Step 6 # MUST
4923        push @{$p->{open_elements}}, [$root, 'html'];
4924    
4925        undef $p->{head_element};
4926    
4927        ## Step 7 # MUST
4928        $p->_reset_insertion_mode;
4929    
4930        ## Step 8 # MUST
4931        my $anode = $node;
4932        AN: while (defined $anode) {
4933          if ($anode->node_type == 1) {
4934            my $nsuri = $anode->namespace_uri;
4935            if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
4936              if ($anode->local_name eq 'form') { ## TODO: case?
4937                $p->{form_element} = $anode;
4938                last AN;
4939              }
4940            }
4941          }
4942          $anode = $anode->parent_node;
4943        } # AN
4944        
4945        ## Step 3 # MUST
4946        ## Step 10 # MUST
4947        {
4948          my $self = $p;
4949          !!!next-token;
4950        }
4951        $p->_tree_construction_main;
4952    
4953        ## Step 11 # MUST
4954        my @cn = @{$node->child_nodes};
4955        for (@cn) {
4956          $node->remove_child ($_);
4957        }
4958        ## ISSUE: mutation events? read-only?
4959    
4960        ## Step 12 # MUST
4961        @cn = @{$root->child_nodes};
4962        for (@cn) {
4963          $node->append_child ($_);
4964        }
4965        ## ISSUE: adopt_node? mutation events?
4966    
4967        $p->_terminate_tree_constructor;
4968      } else {
4969        die "$0: |set_inner_html| is not defined for node of type $nt";
4970      }
4971    } # set_inner_html
4972    
4973    } # tree construction stage
4974    
4975  sub get_inner_html ($$$) {  sub get_inner_html ($$$) {
4976    my ($class, $node, $on_error) = @_;    my (undef, $node, $on_error) = @_;
4977    
4978    ## Step 1    ## Step 1
4979    my $s = '';    my $s = '';

Legend:
Removed from v.1.2  
changed lines
  Added in v.1.6

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24