/[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.3 by wakaba, Wed May 2 13:44:34 2007 UTC
# Line 307  sub parse_string ($$$;$) { Line 307  sub parse_string ($$$;$) {
307    my $s = \$_[0];    my $s = \$_[0];
308    $self->{document} = $_[1];    $self->{document} = $_[1];
309    
310    my $i;    ## NOTE: |set_inner_html| copies most of this method's code
311    
312    my $i = 0;    my $i = 0;
313      my $line = 1;
314      my $column = 0;
315    $self->{set_next_input_character} = sub {    $self->{set_next_input_character} = sub {
316      my $self = shift;      my $self = shift;
317      $self->{next_input_character} = -1 and return if $i >= length $$s;      $self->{next_input_character} = -1 and return if $i >= length $$s;
318      $self->{next_input_character} = ord substr $$s, $i++, 1;      $self->{next_input_character} = ord substr $$s, $i++, 1;
319        $column++;
320            
321      if ($self->{next_input_character} == 0x000D) { # CR      if ($self->{next_input_character} == 0x000D) { # CR
322        if ($i >= length $$s) {        if ($i >= length $$s) {
# Line 326  sub parse_string ($$$;$) { Line 330  sub parse_string ($$$;$) {
330          }          }
331        }        }
332        $self->{next_input_character} = 0x000A; # LF # MUST        $self->{next_input_character} = 0x000A; # LF # MUST
333          $line++;
334          $column = -1;
335      } elsif ($self->{next_input_character} > 0x10FFFF) {      } elsif ($self->{next_input_character} > 0x10FFFF) {
336        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
337      } elsif ($self->{next_input_character} == 0x0000) { # NULL      } elsif ($self->{next_input_character} == 0x0000) { # NULL
# Line 333  sub parse_string ($$$;$) { Line 339  sub parse_string ($$$;$) {
339      }      }
340    };    };
341    
342    $self->{parse_error} = $_[2] || sub {    my $onerror = $_[2] || sub {
343      warn "Parse error at character $i\n"; ## TODO: Report (line, column) pair      my (%opt) = @_;
344        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";
345      };
346      $self->{parse_error} = sub {
347        $onerror->(@_, line => $line, column => $column);
348    };    };
349    
350    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 488  sub _get_next_token ($) { Line 498  sub _get_next_token ($) {
498            !!!next-input-character;            !!!next-input-character;
499            redo A;            redo A;
500          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_input_character} == 0x003E) { # >
501            !!!parse-error;            !!!parse-error (type => 'empty start tag');
502            $self->{state} = 'data';            $self->{state} = 'data';
503            !!!next-input-character;            !!!next-input-character;
504    
# Line 496  sub _get_next_token ($) { Line 506  sub _get_next_token ($) {
506    
507            redo A;            redo A;
508          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_input_character} == 0x003F) { # ?
509            !!!parse-error;            !!!parse-error (type => 'pio');
510            $self->{state} = 'bogus comment';            $self->{state} = 'bogus comment';
511            ## $self->{next_input_character} is intentionally left as is            ## $self->{next_input_character} is intentionally left as is
512            redo A;            redo A;
513          } else {          } else {
514            !!!parse-error;            !!!parse-error (type => 'bare stago');
515            $self->{state} = 'data';            $self->{state} = 'data';
516            ## reconsume            ## reconsume
517    
# Line 524  sub _get_next_token ($) { Line 534  sub _get_next_token ($) {
534              !!!next-input-character;              !!!next-input-character;
535              next TAGNAME;              next TAGNAME;
536            } else {            } else {
537              !!!parse-error;              !!!parse-error (type => 'unmatched end tag');
538              $self->{next_input_character} = shift @next_char; # reconsume              $self->{next_input_character} = shift @next_char; # reconsume
539              !!!back-next-input-character (@next_char);              !!!back-next-input-character (@next_char);
540              $self->{state} = 'data';              $self->{state} = 'data';
# Line 545  sub _get_next_token ($) { Line 555  sub _get_next_token ($) {
555                  $self->{next_input_character} == 0x002F or # /                  $self->{next_input_character} == 0x002F or # /
556                  $self->{next_input_character} == 0x003C or # <                  $self->{next_input_character} == 0x003C or # <
557                  $self->{next_input_character} == -1) {                  $self->{next_input_character} == -1) {
558            !!!parse-error;            !!!parse-error (type => 'unmatched end tag');
559            $self->{next_input_character} = shift @next_char; # reconsume            $self->{next_input_character} = shift @next_char; # reconsume
560            !!!back-next-input-character (@next_char);            !!!back-next-input-character (@next_char);
561            $self->{state} = 'data';            $self->{state} = 'data';
# Line 575  sub _get_next_token ($) { Line 585  sub _get_next_token ($) {
585          !!!next-input-character;          !!!next-input-character;
586          redo A;          redo A;
587        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
588          !!!parse-error;          !!!parse-error (type => 'empty end tag');
589          $self->{state} = 'data';          $self->{state} = 'data';
590          !!!next-input-character;          !!!next-input-character;
591          redo A;          redo A;
592        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
593          !!!parse-error;          !!!parse-error (type => 'bare etago');
594          $self->{state} = 'data';          $self->{state} = 'data';
595          # reconsume          # reconsume
596    
# Line 588  sub _get_next_token ($) { Line 598  sub _get_next_token ($) {
598    
599          redo A;          redo A;
600        } else {        } else {
601          !!!parse-error;          !!!parse-error (type => 'bogus end tag');
602          $self->{state} = 'bogus comment';          $self->{state} = 'bogus comment';
603          ## $self->{next_input_character} is intentionally left as is          ## $self->{next_input_character} is intentionally left as is
604          redo A;          redo A;
# Line 608  sub _get_next_token ($) { Line 618  sub _get_next_token ($) {
618          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
619            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
620            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
621              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
622            }            }
623          } else {          } else {
624            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 639  sub _get_next_token ($) {
639          redo A;          redo A;
640        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
641                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
642          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
643          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
644            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
645          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
646            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
647            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
648              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
649            }            }
650          } else {          } else {
651            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 665  sub _get_next_token ($) {
665            # permitted slash            # permitted slash
666            #            #
667          } else {          } else {
668            !!!parse-error;            !!!parse-error (type => 'nestc');
669          }          }
670          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
671          # next-input-character is already done          # next-input-character is already done
# Line 682  sub _get_next_token ($) { Line 692  sub _get_next_token ($) {
692          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
693            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
694            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
695              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
696            }            }
697          } else {          } else {
698            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 719  sub _get_next_token ($) {
719            # permitted slash            # permitted slash
720            #            #
721          } else {          } else {
722            !!!parse-error;            !!!parse-error (type => 'nestc');
723          }          }
724          ## Stay in the state          ## Stay in the state
725          # next-input-character is already done          # next-input-character is already done
726          redo A;          redo A;
727        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
728                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
729          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
730          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
731            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
732          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
733            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
734            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
735              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
736            }            }
737          } else {          } else {
738            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 755  sub _get_next_token ($) {
755        my $before_leave = sub {        my $before_leave = sub {
756          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
757              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
758            !!!parse-error;            !!!parse-error (type => 'dupulicate attribute');
759            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
760          } else {          } else {
761            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
# Line 774  sub _get_next_token ($) { Line 784  sub _get_next_token ($) {
784          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
785            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
786            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
787              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
788            }            }
789          } else {          } else {
790            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 811  sub _get_next_token ($) {
811            # permitted slash            # permitted slash
812            #            #
813          } else {          } else {
814            !!!parse-error;            !!!parse-error (type => 'nestc');
815          }          }
816          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
817          # next-input-character is already done          # next-input-character is already done
818          redo A;          redo A;
819        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
820                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
821          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
822          $before_leave->();          $before_leave->();
823          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
824            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
825          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
826            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
827            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
828              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
829            }            }
830          } else {          } else {
831            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 862  sub _get_next_token ($) {
862          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
863            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
864            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
865              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
866            }            }
867          } else {          } else {
868            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 889  sub _get_next_token ($) {
889            # permitted slash            # permitted slash
890            #            #
891          } else {          } else {
892            !!!parse-error;            !!!parse-error (type => 'nestc');
893          }          }
894          $self->{state} = 'before attribute name';          $self->{state} = 'before attribute name';
895          # next-input-character is already done          # next-input-character is already done
896          redo A;          redo A;
897        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
898                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
899          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
900          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
901            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
902          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
903            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
904            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
905              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
906            }            }
907          } else {          } else {
908            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 948  sub _get_next_token ($) {
948          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
949            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
950            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
951              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
952            }            }
953          } else {          } else {
954            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 962  sub _get_next_token ($) {
962          redo A;          redo A;
963        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
964                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
965          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
966          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
967            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
968          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
969            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
970            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
971              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
972            }            }
973          } else {          } else {
974            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 997  sub _get_next_token ($) {
997          !!!next-input-character;          !!!next-input-character;
998          redo A;          redo A;
999        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1000          !!!parse-error;          !!!parse-error (type => 'unclosed attribute value');
1001          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1002            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1003          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1004            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1005            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1006              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1007            }            }
1008          } else {          } else {
1009            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 1032  sub _get_next_token ($) {
1032          !!!next-input-character;          !!!next-input-character;
1033          redo A;          redo A;
1034        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1035          !!!parse-error;          !!!parse-error (type => 'unclosed attribute value');
1036          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1037            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1038          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1039            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1040            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1041              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1042            }            }
1043          } else {          } else {
1044            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 1076  sub _get_next_token ($) {
1076          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1077            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1078            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1079              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1080            }            }
1081          } else {          } else {
1082            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 1090  sub _get_next_token ($) {
1090          redo A;          redo A;
1091        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{next_input_character} == 0x003C or # <
1092                 $self->{next_input_character} == -1) {                 $self->{next_input_character} == -1) {
1093          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
1094          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} eq 'start tag') {
1095            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1096          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} eq 'end tag') {
1097            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model_flag} = 'PCDATA'; # MUST
1098            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1099              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
1100            }            }
1101          } else {          } else {
1102            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 1206  sub _get_next_token ($) {
1206          }          }
1207        }        }
1208    
1209        !!!parse-error;        !!!parse-error (type => 'bogus comment open');
1210        $self->{next_input_character} = shift @next_char;        $self->{next_input_character} = shift @next_char;
1211        !!!back-next-input-character (@next_char);        !!!back-next-input-character (@next_char);
1212        $self->{state} = 'bogus comment';        $self->{state} = 'bogus comment';
# Line 1210  sub _get_next_token ($) { Line 1220  sub _get_next_token ($) {
1220          !!!next-input-character;          !!!next-input-character;
1221          redo A;          redo A;
1222        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1223          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1224          $self->{state} = 'data';          $self->{state} = 'data';
1225          ## reconsume          ## reconsume
1226    
# Line 1230  sub _get_next_token ($) { Line 1240  sub _get_next_token ($) {
1240          !!!next-input-character;          !!!next-input-character;
1241          redo A;          redo A;
1242        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1243          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1244          $self->{state} = 'data';          $self->{state} = 'data';
1245          ## reconsume          ## reconsume
1246    
# Line 1254  sub _get_next_token ($) { Line 1264  sub _get_next_token ($) {
1264    
1265          redo A;          redo A;
1266        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_input_character} == 0x002D) { # -
1267          !!!parse-error;          !!!parse-error (type => 'dash in comment');
1268          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
1269          ## Stay in the state          ## Stay in the state
1270          !!!next-input-character;          !!!next-input-character;
1271          redo A;          redo A;
1272        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1273          !!!parse-error;          !!!parse-error (type => 'unclosed comment');
1274          $self->{state} = 'data';          $self->{state} = 'data';
1275          ## reconsume          ## reconsume
1276    
# Line 1269  sub _get_next_token ($) { Line 1279  sub _get_next_token ($) {
1279    
1280          redo A;          redo A;
1281        } else {        } else {
1282          !!!parse-error;          !!!parse-error (type => 'dash in comment');
1283          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment
1284          $self->{state} = 'comment';          $self->{state} = 'comment';
1285          !!!next-input-character;          !!!next-input-character;
# Line 1285  sub _get_next_token ($) { Line 1295  sub _get_next_token ($) {
1295          !!!next-input-character;          !!!next-input-character;
1296          redo A;          redo A;
1297        } else {        } else {
1298          !!!parse-error;          !!!parse-error (type => 'no space before DOCTYPE name');
1299          $self->{state} = 'before DOCTYPE name';          $self->{state} = 'before DOCTYPE name';
1300          ## reconsume          ## reconsume
1301          redo A;          redo A;
# Line 1308  sub _get_next_token ($) { Line 1318  sub _get_next_token ($) {
1318          !!!next-input-character;          !!!next-input-character;
1319          redo A;          redo A;
1320        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_input_character} == 0x003E) { # >
1321          !!!parse-error;          !!!parse-error (type => 'no DOCTYPE name');
1322          $self->{state} = 'data';          $self->{state} = 'data';
1323          !!!next-input-character;          !!!next-input-character;
1324    
# Line 1316  sub _get_next_token ($) { Line 1326  sub _get_next_token ($) {
1326    
1327          redo A;          redo A;
1328        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1329          !!!parse-error;          !!!parse-error (type => 'no DOCTYPE name');
1330          $self->{state} = 'data';          $self->{state} = 'data';
1331          ## reconsume          ## reconsume
1332    
# Line 1358  sub _get_next_token ($) { Line 1368  sub _get_next_token ($) {
1368          !!!next-input-character;          !!!next-input-character;
1369          redo A;          redo A;
1370        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1371          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1372          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE
1373          $self->{state} = 'data';          $self->{state} = 'data';
1374          ## reconsume          ## reconsume
# Line 1393  sub _get_next_token ($) { Line 1403  sub _get_next_token ($) {
1403    
1404          redo A;          redo A;
1405        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1406          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1407          $self->{state} = 'data';          $self->{state} = 'data';
1408          ## reconsume          ## reconsume
1409    
# Line 1402  sub _get_next_token ($) { Line 1412  sub _get_next_token ($) {
1412    
1413          redo A;          redo A;
1414        } else {        } else {
1415          !!!parse-error;          !!!parse-error (type => 'string after DOCTYPE name');
1416          $self->{current_token}->{error} = 1; # DOCTYPE          $self->{current_token}->{error} = 1; # DOCTYPE
1417          $self->{state} = 'bogus DOCTYPE';          $self->{state} = 'bogus DOCTYPE';
1418          !!!next-input-character;          !!!next-input-character;
# Line 1418  sub _get_next_token ($) { Line 1428  sub _get_next_token ($) {
1428    
1429          redo A;          redo A;
1430        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_input_character} == -1) {
1431          !!!parse-error;          !!!parse-error (type => 'unclosed DOCTYPE');
1432          $self->{state} = 'data';          $self->{state} = 'data';
1433          ## reconsume          ## reconsume
1434    
# Line 1471  sub _tokenize_attempt_to_consume_an_enti Line 1481  sub _tokenize_attempt_to_consume_an_enti
1481            $num += $self->{next_input_character} - 0x0040 + 9;            $num += $self->{next_input_character} - 0x0040 + 9;
1482            redo X;            redo X;
1483          } elsif (not defined $num) { # no hexadecimal digit          } elsif (not defined $num) { # no hexadecimal digit
1484            !!!parse-error;            !!!parse-error (type => 'bare hcro');
1485            $self->{next_input_character} = 0x0023; # #            $self->{next_input_character} = 0x0023; # #
1486            !!!back-next-input-character ($x_char);            !!!back-next-input-character ($x_char);
1487            return undef;            return undef;
1488          } elsif ($self->{next_input_character} == 0x003B) { # ;          } elsif ($self->{next_input_character} == 0x003B) { # ;
1489            !!!next-input-character;            !!!next-input-character;
1490          } else {          } else {
1491            !!!parse-error;            !!!parse-error (type => 'no refc');
1492          }          }
1493    
1494          ## TODO: check the definition for |a valid Unicode character|.          ## TODO: check the definition for |a valid Unicode character|.
# Line 1505  sub _tokenize_attempt_to_consume_an_enti Line 1515  sub _tokenize_attempt_to_consume_an_enti
1515        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_input_character} == 0x003B) { # ;
1516          !!!next-input-character;          !!!next-input-character;
1517        } else {        } else {
1518          !!!parse-error;          !!!parse-error (type => 'no refc');
1519        }        }
1520    
1521        ## TODO: check the definition for |a valid Unicode character|.        ## TODO: check the definition for |a valid Unicode character|.
# Line 1516  sub _tokenize_attempt_to_consume_an_enti Line 1526  sub _tokenize_attempt_to_consume_an_enti
1526                
1527        return {type => 'character', data => chr $code};        return {type => 'character', data => chr $code};
1528      } else {      } else {
1529        !!!parse-error;        !!!parse-error (type => 'bare nero');
1530        !!!back-next-input-character ($self->{next_input_character});        !!!back-next-input-character ($self->{next_input_character});
1531        $self->{next_input_character} = 0x0023; # #        $self->{next_input_character} = 0x0023; # #
1532        return undef;        return undef;
# Line 1553  sub _tokenize_attempt_to_consume_an_enti Line 1563  sub _tokenize_attempt_to_consume_an_enti
1563        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_input_character} == 0x003B) { # ;
1564          !!!next-input-character;          !!!next-input-character;
1565        } else {        } else {
1566          !!!parse-error;          !!!parse-error (type => 'refc');
1567        }        }
1568    
1569        return {type => 'character', data => $value};        return {type => 'character', data => $value};
1570      } else {      } else {
1571        !!!parse-error;        !!!parse-error (type => 'bare ero');
1572        ## NOTE: No characters are consumed in the spec.        ## NOTE: No characters are consumed in the spec.
1573        !!!back-token ({type => 'character', data => $value});        !!!back-token ({type => 'character', data => $value});
1574        return undef;        return undef;
1575      }      }
1576    } else {    } else {
1577      ## no characters are consumed      ## no characters are consumed
1578      !!!parse-error;      !!!parse-error (type => 'bare ero');
1579      return undef;      return undef;
1580    }    }
1581  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 1587  sub _terminate_tree_constructor ($) { Line 1597  sub _terminate_tree_constructor ($) {
1597    
1598  ## 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?
1599    
1600    { # tree construction stage
1601      my $token;
1602    
1603  sub _construct_tree ($) {  sub _construct_tree ($) {
1604    my ($self) = @_;    my ($self) = @_;
1605    
# Line 1598  sub _construct_tree ($) { Line 1611  sub _construct_tree ($) {
1611    ## characters and insert one Text node whose data is concatenation    ## characters and insert one Text node whose data is concatenation
1612    ## of all those characters. # MUST    ## of all those characters. # MUST
1613        
   my $token;  
1614    !!!next-token;    !!!next-token;
1615    
1616    my $phase = 'initial'; # MUST    $self->{insertion_mode} = 'before head';
1617      undef $self->{form_element};
1618      undef $self->{head_element};
1619      $self->{open_elements} = [];
1620      undef $self->{inner_html_node};
1621    
1622      $self->_tree_construction_initial; # MUST
1623      $self->_tree_construction_root_element;
1624      $self->_tree_construction_main;
1625    } # _construct_tree
1626    
1627    sub _tree_construction_initial ($) {
1628      my $self = shift;
1629      B: {
1630          if ($token->{type} eq 'DOCTYPE') {
1631            if ($token->{error}) {
1632              ## ISSUE: Spec currently left this case undefined.
1633              !!!parse-error (type => 'bogus DOCTYPE');
1634            }
1635            my $doctype = $self->{document}->create_document_type_definition
1636              ($token->{name});
1637            $self->{document}->append_child ($doctype);
1638            #$phase = 'root element';
1639            !!!next-token;
1640            #redo B;
1641            return;
1642          } elsif ({
1643                    comment => 1,
1644                    'start tag' => 1,
1645                    'end tag' => 1,
1646                    'end-of-file' => 1,
1647                   }->{$token->{type}}) {
1648            ## ISSUE: Spec currently left this case undefined.
1649            !!!parse-error (type => 'missing DOCTYPE');
1650            #$phase = 'root element';
1651            ## reprocess
1652            #redo B;
1653            return;
1654          } elsif ($token->{type} eq 'character') {
1655            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
1656              $self->{document}->manakai_append_text ($1);
1657              ## ISSUE: DOM3 Core does not allow Document > Text
1658              unless (length $token->{data}) {
1659                ## Stay in the phase
1660                !!!next-token;
1661                redo B;
1662              }
1663            }
1664            ## ISSUE: Spec currently left this case undefined.
1665            !!!parse-error (type => 'missing DOCTYPE');
1666            #$phase = 'root element';
1667            ## reprocess
1668            #redo B;
1669            return;
1670          } else {
1671            die "$0: $token->{type}: Unknown token";
1672          }
1673        } # B
1674    } # _tree_construction_initial
1675    
1676    sub _tree_construction_root_element ($) {
1677      my $self = shift;
1678      
1679      B: {
1680          if ($token->{type} eq 'DOCTYPE') {
1681            !!!parse-error (type => 'in html:#DOCTYPE');
1682            ## Ignore the token
1683            ## Stay in the phase
1684            !!!next-token;
1685            redo B;
1686          } elsif ($token->{type} eq 'comment') {
1687            my $comment = $self->{document}->create_comment ($token->{data});
1688            $self->{document}->append_child ($comment);
1689            ## Stay in the phase
1690            !!!next-token;
1691            redo B;
1692          } elsif ($token->{type} eq 'character') {
1693            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
1694              $self->{document}->manakai_append_text ($1);
1695              ## ISSUE: DOM3 Core does not allow Document > Text
1696              unless (length $token->{data}) {
1697                ## Stay in the phase
1698                !!!next-token;
1699                redo B;
1700              }
1701            }
1702            #
1703          } elsif ({
1704                    'start tag' => 1,
1705                    'end tag' => 1,
1706                    'end-of-file' => 1,
1707                   }->{$token->{type}}) {
1708            ## ISSUE: There is an issue in the spec
1709            #
1710          } else {
1711            die "$0: $token->{type}: Unknown token";
1712          }
1713          my $root_element; !!!create-element ($root_element, 'html');
1714          $self->{document}->append_child ($root_element);
1715          push @{$self->{open_elements}}, [$root_element, 'html'];
1716          #$phase = 'main';
1717          ## reprocess
1718          #redo B;
1719          return;
1720      } # B
1721    } # _tree_construction_root_element
1722    
1723    sub _reset_insertion_mode ($) {
1724      my $self = shift;
1725    
1726        ## Step 1
1727        my $last;
1728        
1729        ## Step 2
1730        my $i = -1;
1731        my $node = $self->{open_elements}->[$i];
1732        
1733        ## Step 3
1734        S3: {
1735          $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];
1736          if (defined $self->{inner_html_node}) {
1737            if ($self->{inner_html_node}->[1] eq 'td' or
1738                $self->{inner_html_node}->[1] eq 'th') {
1739              #
1740            } else {
1741              $node = $self->{inner_html_node};
1742            }
1743          }
1744        
1745          ## Step 4..13
1746          my $new_mode = {
1747                          select => 'in select',
1748                          td => 'in cell',
1749                          th => 'in cell',
1750                          tr => 'in row',
1751                          tbody => 'in table body',
1752                          thead => 'in table head',
1753                          tfoot => 'in table foot',
1754                          caption => 'in caption',
1755                          colgroup => 'in column group',
1756                          table => 'in table',
1757                          head => 'in body', # not in head!
1758                          body => 'in body',
1759                          frameset => 'in frameset',
1760                         }->{$node->[1]};
1761          $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1762          
1763          ## Step 14
1764          if ($node->[1] eq 'html') {
1765            unless (defined $self->{head_element}) {
1766              $self->{insertion_mode} = 'before head';
1767            } else {
1768              $self->{insertion_mode} = 'after head';
1769            }
1770            return;
1771          }
1772          
1773          ## Step 15
1774          $self->{insertion_mode} = 'in body' and return if $last;
1775          
1776          ## Step 16
1777          $i--;
1778          $node = $self->{open_elements}->[$i];
1779          
1780          ## Step 17
1781          redo S3;
1782        } # S3
1783    } # _reset_insertion_mode
1784    
1785    sub _tree_construction_main ($) {
1786      my $self = shift;
1787    
1788      my $phase = 'main';
1789    
   my $open_elements = [];  
1790    my $active_formatting_elements = [];    my $active_formatting_elements = [];
   my $head_element;  
   my $form_element;  
   my $insertion_mode = 'before head';  
1791    
1792    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
1793      my $insert = shift;      my $insert = shift;
# Line 1621  sub _construct_tree ($) { Line 1801  sub _construct_tree ($) {
1801    
1802      ## Step 2      ## Step 2
1803      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
1804      for (@$open_elements) {      for (@{$self->{open_elements}}) {
1805        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
1806          return;          return;
1807        }        }
# Line 1640  sub _construct_tree ($) { Line 1820  sub _construct_tree ($) {
1820          #          #
1821        } else {        } else {
1822          my $in_open_elements;          my $in_open_elements;
1823          OE: for (@$open_elements) {          OE: for (@{$self->{open_elements}}) {
1824            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
1825              $in_open_elements = 1;              $in_open_elements = 1;
1826              last OE;              last OE;
# Line 1664  sub _construct_tree ($) { Line 1844  sub _construct_tree ($) {
1844            
1845        ## Step 9        ## Step 9
1846        $insert->($clone->[0]);        $insert->($clone->[0]);
1847        push @$open_elements, $clone;        push @{$self->{open_elements}}, $clone;
1848                
1849        ## Step 10        ## Step 10
1850        $active_formatting_elements->[$i] = $open_elements->[-1];        $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
1851    
1852        ## Step 11        ## Step 11
1853        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
# Line 1689  sub _construct_tree ($) { Line 1869  sub _construct_tree ($) {
1869      }      }
1870    }; # $clear_up_to_marker    }; # $clear_up_to_marker
1871    
   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  
   
1872    my $style_start_tag = sub {    my $style_start_tag = sub {
1873      my $style_el; !!!create-element ($style_el, 'style');      my $style_el; !!!create-element ($style_el, 'style');
1874      ## $insertion_mode eq 'in head' and ... (always true)      ## $self->{insertion_mode} eq 'in head' and ... (always true)
1875      (($insertion_mode eq 'in head' and defined $head_element)      (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})
1876       ? $head_element : $open_elements->[-1]->[0])       ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
1877        ->append_child ($style_el);        ->append_child ($style_el);
1878      $self->{content_model_flag} = 'CDATA';      $self->{content_model_flag} = 'CDATA';
1879                                
# Line 1765  sub _construct_tree ($) { Line 1892  sub _construct_tree ($) {
1892      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {
1893        ## Ignore the token        ## Ignore the token
1894      } else {      } else {
1895        !!!parse-error;        !!!parse-error (type => 'in CDATA:#'.$token->{type});
1896        ## ISSUE: And ignore?        ## ISSUE: And ignore?
1897      }      }
1898      !!!next-token;      !!!next-token;
# Line 1794  sub _construct_tree ($) { Line 1921  sub _construct_tree ($) {
1921          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
1922        ## Ignore the token        ## Ignore the token
1923      } else {      } else {
1924        !!!parse-error;        !!!parse-error (type => 'in CDATA:#'.$token->{type});
1925        ## ISSUE: And ignore?        ## ISSUE: And ignore?
1926        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
1927      }      }
1928            
1929      ## TODO: inner_html mode then mark as "already executed" and skip      if (defined $self->{inner_html_node}) {
1930      if (1) {        ## TODO: mark as "already executed"
1931        } else {
1932        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
1933        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
1934                
1935        (($insertion_mode eq 'in head' and defined $head_element)        (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})
1936         ? $head_element : $open_elements->[-1]->[0])->append_child ($script_el);         ? $self->{head_element} : $self->{open_elements}->[-1]->[0])->append_child ($script_el);
1937                
1938        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
1939                
# Line 1832  sub _construct_tree ($) { Line 1960  sub _construct_tree ($) {
1960          }          }
1961        } # AFE        } # AFE
1962        unless (defined $formatting_element) {        unless (defined $formatting_element) {
1963          !!!parse-error;          !!!parse-error (type => 'unmatched end tag:'.$tag_name);
1964          ## Ignore the token          ## Ignore the token
1965          !!!next-token;          !!!next-token;
1966          return;          return;
# Line 1840  sub _construct_tree ($) { Line 1968  sub _construct_tree ($) {
1968        ## has an element in scope        ## has an element in scope
1969        my $in_scope = 1;        my $in_scope = 1;
1970        my $formatting_element_i_in_open;          my $formatting_element_i_in_open;  
1971        INSCOPE: for (reverse 0..$#$open_elements) {        INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
1972          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
1973          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
1974            if ($in_scope) {            if ($in_scope) {
1975              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
# Line 1865  sub _construct_tree ($) { Line 1993  sub _construct_tree ($) {
1993          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
1994          return;          return;
1995        }        }
1996        if (not $open_elements->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
1997          !!!parse-error;          !!!parse-error;
1998        }        }
1999                
2000        ## Step 2        ## Step 2
2001        my $furthest_block;        my $furthest_block;
2002        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
2003        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
2004          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
2005          if (not $formatting_category->{$node->[1]} and          if (not $formatting_category->{$node->[1]} and
2006              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
2007              ($special_category->{$node->[1]} or              ($special_category->{$node->[1]} or
# Line 1887  sub _construct_tree ($) { Line 2015  sub _construct_tree ($) {
2015                
2016        ## Step 3        ## Step 3
2017        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
2018          splice @$open_elements, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
2019          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
2020          !!!next-token;          !!!next-token;
2021          return;          return;
2022        }        }
2023                
2024        ## Step 4        ## Step 4
2025        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];
2026                
2027        ## Step 5        ## Step 5
2028        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
# Line 1914  sub _construct_tree ($) { Line 2042  sub _construct_tree ($) {
2042        S7: {        S7: {
2043          ## Step 1          ## Step 1
2044          $node_i_in_open--;          $node_i_in_open--;
2045          $node = $open_elements->[$node_i_in_open];          $node = $self->{open_elements}->[$node_i_in_open];
2046                    
2047          ## Step 2          ## Step 2
2048          my $node_i_in_active;          my $node_i_in_active;
# Line 1925  sub _construct_tree ($) { Line 2053  sub _construct_tree ($) {
2053                last S7S2;                last S7S2;
2054              }              }
2055            }            }
2056            splice @$open_elements, $node_i_in_open, 1;            splice @{$self->{open_elements}}, $node_i_in_open, 1;
2057            redo S7;            redo S7;
2058          } # S7S2          } # S7S2
2059                    
# Line 1941  sub _construct_tree ($) { Line 2069  sub _construct_tree ($) {
2069          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
2070            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
2071            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
2072            $open_elements->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
2073            $node = $clone;            $node = $clone;
2074          }          }
2075                    
# Line 1983  sub _construct_tree ($) { Line 2111  sub _construct_tree ($) {
2111                
2112        ## Step 13        ## Step 13
2113        undef $i;        undef $i;
2114        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
2115          if ($open_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
2116            splice @$open_elements, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
2117            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
2118          } elsif ($open_elements->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
2119            $i = $_;            $i = $_;
2120          }          }
2121        } # OE        } # OE
2122        splice @$open_elements, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 1, $clone;
2123                
2124        ## Step 14        ## Step 14
2125        redo FET;        redo FET;
# Line 1999  sub _construct_tree ($) { Line 2127  sub _construct_tree ($) {
2127    }; # $formatting_end_tag    }; # $formatting_end_tag
2128    
2129    my $insert_to_current = sub {    my $insert_to_current = sub {
2130      $open_elements->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child (shift);
2131    }; # $insert_to_current    }; # $insert_to_current
2132    
2133    my $insert_to_foster = sub {    my $insert_to_foster = sub {
# Line 2007  sub _construct_tree ($) { Line 2135  sub _construct_tree ($) {
2135                         if ({                         if ({
2136                              table => 1, tbody => 1, tfoot => 1,                              table => 1, tbody => 1, tfoot => 1,
2137                              thead => 1, tr => 1,                              thead => 1, tr => 1,
2138                             }->{$open_elements->[-1]->[1]}) {                             }->{$self->{open_elements}->[-1]->[1]}) {
2139                           # MUST                           # MUST
2140                           my $foster_parent_element;                           my $foster_parent_element;
2141                           my $next_sibling;                           my $next_sibling;
2142                           OE: for (reverse 0..$#$open_elements) {                           OE: for (reverse 0..$#{$self->{open_elements}}) {
2143                             if ($open_elements->[$_]->[1] eq 'table') {                             if ($self->{open_elements}->[$_]->[1] eq 'table') {
2144                               my $parent = $open_elements->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
2145                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
2146                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
2147                                 $next_sibling = $open_elements->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
2148                               } else {                               } else {
2149                                 $foster_parent_element                                 $foster_parent_element
2150                                   = $open_elements->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
2151                               }                               }
2152                               last OE;                               last OE;
2153                             }                             }
2154                           } # OE                           } # OE
2155                           $foster_parent_element = $open_elements->[0]->[0]                           $foster_parent_element = $self->{open_elements}->[0]->[0]
2156                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
2157                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
2158                             ($child, $next_sibling);                             ($child, $next_sibling);
2159                         } else {                         } else {
2160                           $open_elements->[-1]->[0]->append_child ($child);                           $self->{open_elements}->[-1]->[0]->append_child ($child);
2161                         }                         }
2162    }; # $insert_to_foster    }; # $insert_to_foster
2163    
# Line 2045  sub _construct_tree ($) { Line 2173  sub _construct_tree ($) {
2173        } elsif ({        } elsif ({
2174                  base => 1, link => 1, meta => 1,                  base => 1, link => 1, meta => 1,
2175                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2176          !!!parse-error ($token->{tag_name}.' in body');          !!!parse-error (type => 'in body:'.$token->{tag_name});
2177          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
2178          my $el;          my $el;
2179          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes});
2180          if (defined $head_element) {          if (defined $self->{head_element}) {
2181            $head_element->append_child ($el);            $self->{head_element}->append_child ($el);
2182          } else {          } else {
2183            $insert->($el);            $insert->($el);
2184          }          }
# Line 2058  sub _construct_tree ($) { Line 2186  sub _construct_tree ($) {
2186          !!!next-token;          !!!next-token;
2187          return;          return;
2188        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
2189          !!!parse-error ('title in body');          !!!parse-error (type => 'in body:title');
2190          ## NOTE: There is an "as if in head" code clone          ## NOTE: There is an "as if in head" code clone
2191          my $title_el;          my $title_el;
2192          !!!create-element ($title_el, 'title', $token->{attributes});          !!!create-element ($title_el, 'title', $token->{attributes});
2193          (defined $head_element ? $head_element : $open_elements->[-1]->[0])          (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
2194            ->append_child ($title_el);            ->append_child ($title_el);
2195          $self->{content_model_flag} = 'RCDATA';          $self->{content_model_flag} = 'RCDATA';
2196                    
# Line 2082  sub _construct_tree ($) { Line 2210  sub _construct_tree ($) {
2210              $token->{tag_name} eq 'title') {              $token->{tag_name} eq 'title') {
2211            ## Ignore the token            ## Ignore the token
2212          } else {          } else {
2213            !!!parse-error;            !!!parse-error (type => 'in RCDATA:#'.$token->{type});
2214            ## ISSUE: And ignore?            ## ISSUE: And ignore?
2215          }          }
2216          !!!next-token;          !!!next-token;
2217          return;          return;
2218        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
2219          !!!parse-error;          !!!parse-error (type => 'in body:body');
2220                                
2221          if (@$open_elements == 1 or          if (@{$self->{open_elements}} == 1 or
2222              $open_elements->[1]->[1] ne 'body') {              $self->{open_elements}->[1]->[1] ne 'body') {
2223            ## Ignore the token            ## Ignore the token
2224          } else {          } else {
2225            my $body_el = $open_elements->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
2226            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
2227              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
2228                $body_el->set_attribute_ns                $body_el->set_attribute_ns
# Line 2112  sub _construct_tree ($) { Line 2240  sub _construct_tree ($) {
2240                  pre => 1,                  pre => 1,
2241                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2242          ## has a p element in scope          ## has a p element in scope
2243          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2244            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2245              !!!back-token;              !!!back-token;
2246              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2139  sub _construct_tree ($) { Line 2267  sub _construct_tree ($) {
2267          }          }
2268          return;          return;
2269        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
2270          if (defined $form_element) {          if (defined $self->{form_element}) {
2271            !!!parse-error;            !!!parse-error (type => 'in form:form');
2272            ## Ignore the token            ## Ignore the token
2273          } else {          } else {
2274            ## has a p element in scope            ## has a p element in scope
2275            INSCOPE: for (reverse @$open_elements) {            INSCOPE: for (reverse @{$self->{open_elements}}) {
2276              if ($_->[1] eq 'p') {              if ($_->[1] eq 'p') {
2277                !!!back-token;                !!!back-token;
2278                $token = {type => 'end tag', tag_name => 'p'};                $token = {type => 'end tag', tag_name => 'p'};
# Line 2158  sub _construct_tree ($) { Line 2286  sub _construct_tree ($) {
2286            } # INSCOPE            } # INSCOPE
2287                            
2288            !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2289            $form_element = $open_elements->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
2290            !!!next-token;            !!!next-token;
2291            return;            return;
2292          }          }
2293        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
2294          ## has a p element in scope          ## has a p element in scope
2295          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2296            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2297              !!!back-token;              !!!back-token;
2298              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2179  sub _construct_tree ($) { Line 2307  sub _construct_tree ($) {
2307                        
2308          ## Step 1          ## Step 1
2309          my $i = -1;          my $i = -1;
2310          my $node = $open_elements->[$i];          my $node = $self->{open_elements}->[$i];
2311          LI: {          LI: {
2312            ## Step 2            ## Step 2
2313            if ($node->[1] eq 'li') {            if ($node->[1] eq 'li') {
2314              splice @$open_elements, $i;              splice @{$self->{open_elements}}, $i;
2315              last LI;              last LI;
2316            }            }
2317                        
# Line 2198  sub _construct_tree ($) { Line 2326  sub _construct_tree ($) {
2326                        
2327            ## Step 4            ## Step 4
2328            $i--;            $i--;
2329            $node = $open_elements->[$i];            $node = $self->{open_elements}->[$i];
2330            redo LI;            redo LI;
2331          } # LI          } # LI
2332                        
# Line 2207  sub _construct_tree ($) { Line 2335  sub _construct_tree ($) {
2335          return;          return;
2336        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {        } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {
2337          ## has a p element in scope          ## has a p element in scope
2338          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2339            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2340              !!!back-token;              !!!back-token;
2341              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2222  sub _construct_tree ($) { Line 2350  sub _construct_tree ($) {
2350                        
2351          ## Step 1          ## Step 1
2352          my $i = -1;          my $i = -1;
2353          my $node = $open_elements->[$i];          my $node = $self->{open_elements}->[$i];
2354          LI: {          LI: {
2355            ## Step 2            ## Step 2
2356            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {
2357              splice @$open_elements, $i;              splice @{$self->{open_elements}}, $i;
2358              last LI;              last LI;
2359            }            }
2360                        
# Line 2241  sub _construct_tree ($) { Line 2369  sub _construct_tree ($) {
2369                        
2370            ## Step 4            ## Step 4
2371            $i--;            $i--;
2372            $node = $open_elements->[$i];            $node = $self->{open_elements}->[$i];
2373            redo LI;            redo LI;
2374          } # LI          } # LI
2375                        
# Line 2250  sub _construct_tree ($) { Line 2378  sub _construct_tree ($) {
2378          return;          return;
2379        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
2380          ## has a p element in scope          ## has a p element in scope
2381          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2382            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2383              !!!back-token;              !!!back-token;
2384              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2273  sub _construct_tree ($) { Line 2401  sub _construct_tree ($) {
2401                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2402                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2403          ## has a p element in scope          ## has a p element in scope
2404          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2405            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2406            if ($node->[1] eq 'p') {            if ($node->[1] eq 'p') {
2407              !!!back-token;              !!!back-token;
2408              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2289  sub _construct_tree ($) { Line 2417  sub _construct_tree ($) {
2417                        
2418          ## has an element in scope          ## has an element in scope
2419          my $i;          my $i;
2420          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2421            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2422            if ({            if ({
2423                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2424                }->{$node->[1]}) {                }->{$node->[1]}) {
# Line 2305  sub _construct_tree ($) { Line 2433  sub _construct_tree ($) {
2433          } # INSCOPE          } # INSCOPE
2434                        
2435          if (defined $i) {          if (defined $i) {
2436            !!!parse-error;            !!!parse-error (type => 'in hn:hn');
2437            splice @$open_elements, $i;            splice @{$self->{open_elements}}, $i;
2438          }          }
2439                        
2440          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
# Line 2317  sub _construct_tree ($) { Line 2445  sub _construct_tree ($) {
2445          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
2446            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
2447            if ($node->[1] eq 'a') {            if ($node->[1] eq 'a') {
2448              !!!parse-error ('a in a');              !!!parse-error (type => 'in a:a');
2449                            
2450              !!!back-token;              !!!back-token;
2451              $token = {type => 'end tag', tag_name => 'a'};              $token = {type => 'end tag', tag_name => 'a'};
# Line 2329  sub _construct_tree ($) { Line 2457  sub _construct_tree ($) {
2457                  last AFE2;                  last AFE2;
2458                }                }
2459              } # AFE2              } # AFE2
2460              OE: for (reverse 0..$#$open_elements) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
2461                if ($open_elements->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
2462                  splice @$open_elements, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
2463                  last OE;                  last OE;
2464                }                }
2465              } # OE              } # OE
# Line 2344  sub _construct_tree ($) { Line 2472  sub _construct_tree ($) {
2472          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2473    
2474          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2475          push @$active_formatting_elements, $open_elements->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
2476    
2477          !!!next-token;          !!!next-token;
2478          return;          return;
# Line 2356  sub _construct_tree ($) { Line 2484  sub _construct_tree ($) {
2484          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2485                    
2486          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2487          push @$active_formatting_elements, $open_elements->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
2488                    
2489          !!!next-token;          !!!next-token;
2490          return;          return;
2491        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
2492          ## has a button element in scope          ## has a button element in scope
2493          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2494            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2495            if ($node->[1] eq 'button') {            if ($node->[1] eq 'button') {
2496              !!!parse-error;              !!!parse-error (type => 'in button:button');
2497              !!!back-token;              !!!back-token;
2498              $token = {type => 'end tag', tag_name => 'button'};              $token = {type => 'end tag', tag_name => 'button'};
2499              return;              return;
# Line 2404  sub _construct_tree ($) { Line 2532  sub _construct_tree ($) {
2532          return;          return;
2533        } elsif ($token->{tag_name} eq 'table') {        } elsif ($token->{tag_name} eq 'table') {
2534          ## has a p element in scope          ## has a p element in scope
2535          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2536            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2537              !!!back-token;              !!!back-token;
2538              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2419  sub _construct_tree ($) { Line 2547  sub _construct_tree ($) {
2547                        
2548          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2549                        
2550          $insertion_mode = 'in table';          $self->{insertion_mode} = 'in table';
2551                        
2552          !!!next-token;          !!!next-token;
2553          return;          return;
# Line 2429  sub _construct_tree ($) { Line 2557  sub _construct_tree ($) {
2557                  image => 1,                  image => 1,
2558                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2559          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
2560            !!!parse-error;            !!!parse-error (type => 'image');
2561            $token->{tag_name} = 'img';            $token->{tag_name} = 'img';
2562          }          }
2563                    
2564          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2565                    
2566          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2567          pop @$open_elements;          pop @{$self->{open_elements}};
2568                    
2569          !!!next-token;          !!!next-token;
2570          return;          return;
2571        } elsif ($token->{tag_name} eq 'hr') {        } elsif ($token->{tag_name} eq 'hr') {
2572          ## has a p element in scope          ## has a p element in scope
2573          INSCOPE: for (reverse @$open_elements) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
2574            if ($_->[1] eq 'p') {            if ($_->[1] eq 'p') {
2575              !!!back-token;              !!!back-token;
2576              $token = {type => 'end tag', tag_name => 'p'};              $token = {type => 'end tag', tag_name => 'p'};
# Line 2456  sub _construct_tree ($) { Line 2584  sub _construct_tree ($) {
2584          } # INSCOPE          } # INSCOPE
2585                        
2586          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2587          pop @$open_elements;          pop @{$self->{open_elements}};
2588                        
2589          !!!next-token;          !!!next-token;
2590          return;          return;
# Line 2464  sub _construct_tree ($) { Line 2592  sub _construct_tree ($) {
2592          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
2593                    
2594          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2595          ## TODO: associate with $form_element if defined          ## TODO: associate with $self->{form_element} if defined
2596          pop @$open_elements;          pop @{$self->{open_elements}};
2597                    
2598          !!!next-token;          !!!next-token;
2599          return;          return;
2600        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
2601          !!!parse-error;          !!!parse-error (type => 'isindex');
2602                    
2603          if (defined $form_element) {          if (defined $self->{form_element}) {
2604            ## Ignore the token            ## Ignore the token
2605            !!!next-token;            !!!next-token;
2606            return;            return;
# Line 2509  sub _construct_tree ($) { Line 2637  sub _construct_tree ($) {
2637          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $token->{tag_name}, $token->{attributes});
2638                    
2639          if ($token->{tag_name} eq 'textarea') {          if ($token->{tag_name} eq 'textarea') {
2640            ## TODO: form_element if defined            ## TODO: $self->{form_element} if defined
2641            $self->{content_model_flag} = 'RCDATA';            $self->{content_model_flag} = 'RCDATA';
2642          } else {          } else {
2643            $self->{content_model_flag} = 'CDATA';            $self->{content_model_flag} = 'CDATA';
# Line 2533  sub _construct_tree ($) { Line 2661  sub _construct_tree ($) {
2661              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
2662            ## Ignore the token            ## Ignore the token
2663          } else {          } else {
2664            !!!parse-error;            if ($token->{tag_name} eq 'textarea') {
2665                !!!parse-error (type => 'in CDATA:#'.$token->{type});
2666              } else {
2667                !!!parse-error (type => 'in RCDATA:#'.$token->{type});
2668              }
2669            ## ISSUE: And ignore?            ## ISSUE: And ignore?
2670          }          }
2671          !!!next-token;          !!!next-token;
# Line 2543  sub _construct_tree ($) { Line 2675  sub _construct_tree ($) {
2675                    
2676          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes});
2677                    
2678          $insertion_mode = 'in select';          $self->{insertion_mode} = 'in select';
2679          !!!next-token;          !!!next-token;
2680          return;          return;
2681        } elsif ({        } elsif ({
# Line 2552  sub _construct_tree ($) { Line 2684  sub _construct_tree ($) {
2684                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
2685                  thead => 1, tr => 1,                  thead => 1, tr => 1,
2686                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2687          !!!parse-error;          !!!parse-error (type => 'in body:'.$token->{tag_name});
2688          ## Ignore the token          ## Ignore the token
2689          !!!next-token;          !!!next-token;
2690          return;          return;
# Line 2568  sub _construct_tree ($) { Line 2700  sub _construct_tree ($) {
2700        }        }
2701      } elsif ($token->{type} eq 'end tag') {      } elsif ($token->{type} eq 'end tag') {
2702        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
2703          if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
2704            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
2705            if ($open_elements->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
2706              !!!parse-error;              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2707            }            }
2708            $insertion_mode = 'after body';            $self->{insertion_mode} = 'after body';
2709            !!!next-token;            !!!next-token;
2710            return;            return;
2711          } else {          } else {
2712            !!!parse-error;            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2713            ## Ignore the token            ## Ignore the token
2714            !!!next-token;            !!!next-token;
2715            return;            return;
2716          }          }
2717        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
2718          if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
2719            ## ISSUE: There is an issue in the spec.            ## ISSUE: There is an issue in the spec.
2720            if ($open_elements->[-1]->[1] ne 'body') {            if ($self->{open_elements}->[-1]->[1] ne 'body') {
2721              !!!parse-error;              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);
2722            }            }
2723            $insertion_mode = 'after body';            $self->{insertion_mode} = 'after body';
2724            ## reprocess            ## reprocess
2725            return;            return;
2726          } else {          } else {
2727            !!!parse-error;            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2728            ## Ignore the token            ## Ignore the token
2729            !!!next-token;            !!!next-token;
2730            return;            return;
# Line 2608  sub _construct_tree ($) { Line 2740  sub _construct_tree ($) {
2740                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2741          ## has an element in scope          ## has an element in scope
2742          my $i;          my $i;
2743          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2744            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2745            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] eq $token->{tag_name}) {
2746              ## generate implied end tags              ## generate implied end tags
2747              if ({              if ({
# Line 2618  sub _construct_tree ($) { Line 2750  sub _construct_tree ($) {
2750                   li => ($token->{tag_name} ne 'li'),                   li => ($token->{tag_name} ne 'li'),
2751                   p => ($token->{tag_name} ne 'p'),                   p => ($token->{tag_name} ne 'p'),
2752                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2753                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2754                !!!back-token;                !!!back-token;
2755                $token = {type => 'end tag',                $token = {type => 'end tag',
2756                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2757                return;                return;
2758              }              }
2759              $i = $_;              $i = $_;
# Line 2634  sub _construct_tree ($) { Line 2766  sub _construct_tree ($) {
2766            }            }
2767          } # INSCOPE          } # INSCOPE
2768                    
2769          if ($open_elements->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
2770            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2771          }          }
2772                    
2773          splice @$open_elements, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
2774          undef $form_element if $token->{tag_name} eq 'form';          undef $self->{form_element} if $token->{tag_name} eq 'form';
2775          $clear_up_to_marker->()          $clear_up_to_marker->()
2776            if {            if {
2777              button => 1, marquee => 1, object => 1,              button => 1, marquee => 1, object => 1,
# Line 2651  sub _construct_tree ($) { Line 2783  sub _construct_tree ($) {
2783                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2784          ## has an element in scope          ## has an element in scope
2785          my $i;          my $i;
2786          INSCOPE: for (reverse 0..$#$open_elements) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2787            my $node = $open_elements->[$_];            my $node = $self->{open_elements}->[$_];
2788            if ({            if ({
2789                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
2790                }->{$node->[1]}) {                }->{$node->[1]}) {
# Line 2660  sub _construct_tree ($) { Line 2792  sub _construct_tree ($) {
2792              if ({              if ({
2793                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
2794                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2795                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2796                !!!back-token;                !!!back-token;
2797                $token = {type => 'end tag',                $token = {type => 'end tag',
2798                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2799                return;                return;
2800              }              }
2801              $i = $_;              $i = $_;
# Line 2676  sub _construct_tree ($) { Line 2808  sub _construct_tree ($) {
2808            }            }
2809          } # INSCOPE          } # INSCOPE
2810                    
2811          if ($open_elements->[-1]->[1] ne $token->{tag_name}) {          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
2812            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2813          }          }
2814                    
2815          splice @$open_elements, $i if defined $i;          splice @{$self->{open_elements}}, $i if defined $i;
2816          !!!next-token;          !!!next-token;
2817          return;          return;
2818        } elsif ({        } elsif ({
# Line 2703  sub _construct_tree ($) { Line 2835  sub _construct_tree ($) {
2835                  table => 1, textarea => 1, wbr => 1,                  table => 1, textarea => 1, wbr => 1,
2836                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
2837                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
2838          !!!parse-error;          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2839          ## Ignore the token          ## Ignore the token
2840          !!!next-token;          !!!next-token;
2841          return;          return;
# Line 2713  sub _construct_tree ($) { Line 2845  sub _construct_tree ($) {
2845        } else {        } else {
2846          ## Step 1          ## Step 1
2847          my $node_i = -1;          my $node_i = -1;
2848          my $node = $open_elements->[$node_i];          my $node = $self->{open_elements}->[$node_i];
2849    
2850          ## Step 2          ## Step 2
2851          S2: {          S2: {
# Line 2723  sub _construct_tree ($) { Line 2855  sub _construct_tree ($) {
2855              if ({              if ({
2856                   dd => 1, dt => 1, li => 1, p => 1,                   dd => 1, dt => 1, li => 1, p => 1,
2857                   td => 1, th => 1, tr => 1,                   td => 1, th => 1, tr => 1,
2858                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
2859                !!!back-token;                !!!back-token;
2860                $token = {type => 'end tag',                $token = {type => 'end tag',
2861                          tag_name => $open_elements->[-1]->[1]}; # MUST                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
2862                return;                return;
2863              }              }
2864                    
2865              ## Step 2              ## Step 2
2866              if ($token->{tag_name} ne $open_elements->[-1]->[1]) {              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
2867                !!!parse-error;                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2868              }              }
2869                            
2870              ## Step 3              ## Step 3
2871              splice @$open_elements, $node_i;              splice @{$self->{open_elements}}, $node_i;
2872    
2873                !!!next-token;
2874              last S2;              last S2;
2875            } else {            } else {
2876              ## Step 3              ## Step 3
# Line 2744  sub _construct_tree ($) { Line 2878  sub _construct_tree ($) {
2878                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
2879                  ($special_category->{$node->[1]} or                  ($special_category->{$node->[1]} or
2880                   $scoping_category->{$node->[1]})) {                   $scoping_category->{$node->[1]})) {
2881                !!!parse-error;                !!!parse-error (type => 'not closed:'.$node->[1]);
2882                ## Ignore the token                ## Ignore the token
2883                !!!next-token;                !!!next-token;
2884                last S2;                last S2;
# Line 2753  sub _construct_tree ($) { Line 2887  sub _construct_tree ($) {
2887                        
2888            ## Step 4            ## Step 4
2889            $node_i--;            $node_i--;
2890            $node = $open_elements->[$node_i];            $node = $self->{open_elements}->[$node_i];
2891                        
2892            ## Step 5;            ## Step 5;
2893            redo S2;            redo S2;
2894          } # S2          } # S2
2895            return;
2896        }        }
2897      }      }
2898    }; # $in_body    }; # $in_body
2899    
2900    B: {    B: {
2901      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') {  
2902        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} eq 'DOCTYPE') {
2903          !!!parse-error;          !!!parse-error (type => 'in html:#DOCTYPE');
         ## 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') {  
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error;  
2904          ## Ignore the token          ## Ignore the token
2905          ## Stay in the phase          ## Stay in the phase
2906          !!!next-token;          !!!next-token;
# Line 2854  sub _construct_tree ($) { Line 2908  sub _construct_tree ($) {
2908        } elsif ($token->{type} eq 'start tag' and        } elsif ($token->{type} eq 'start tag' and
2909                 $token->{tag_name} eq 'html') {                 $token->{tag_name} eq 'html') {
2910          ## TODO: unless it is the first start tag token, parse-error          ## TODO: unless it is the first start tag token, parse-error
2911          my $top_el = $open_elements->[0]->[0];          my $top_el = $self->{open_elements}->[0]->[0];
2912          for my $attr_name (keys %{$token->{attributes}}) {          for my $attr_name (keys %{$token->{attributes}}) {
2913            unless ($top_el->has_attribute_ns (undef, $attr_name)) {            unless ($top_el->has_attribute_ns (undef, $attr_name)) {
2914              $top_el->set_attribute_ns              $top_el->set_attribute_ns
# Line 2868  sub _construct_tree ($) { Line 2922  sub _construct_tree ($) {
2922          ## Generate implied end tags          ## Generate implied end tags
2923          if ({          if ({
2924               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,
2925              }->{$open_elements->[-1]->[1]}) {              }->{$self->{open_elements}->[-1]->[1]}) {
2926            !!!back-token;            !!!back-token;
2927            $token = {type => 'end tag', tag_name => $open_elements->[-1]->[1]};            $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};
2928            redo B;            redo B;
2929          }          }
2930                    
2931          if (@$open_elements > 2 or          if (@{$self->{open_elements}} > 2 or
2932              (@$open_elements == 2 and $open_elements->[1]->[1] ne 'body')) {              (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {
2933            !!!parse-error;            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2934          } else {          } elsif (defined $self->{inner_html_node} and
2935            ## TODO: inner_html parser and @$open_elements > 1 and $open_elements->[1] ne 'body', then parse-error                   @{$self->{open_elements}} > 1 and
2936                     $self->{open_elements}->[1]->[1] ne 'body') {
2937              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
2938          }          }
2939    
2940          ## Stop parsing          ## Stop parsing
# Line 2886  sub _construct_tree ($) { Line 2942  sub _construct_tree ($) {
2942    
2943          ## ISSUE: There is an issue in the spec.          ## ISSUE: There is an issue in the spec.
2944        } else {        } else {
2945          if ($insertion_mode eq 'before head') {          if ($self->{insertion_mode} eq 'before head') {
2946            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
2947              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
2948                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2949                unless (length $token->{data}) {                unless (length $token->{data}) {
2950                  !!!next-token;                  !!!next-token;
2951                  redo B;                  redo B;
2952                }                }
2953              }              }
2954              ## As if <head>              ## As if <head>
2955              !!!create-element ($head_element, 'head');              !!!create-element ($self->{head_element}, 'head');
2956              $open_elements->[-1]->[0]->append_child ($head_element);              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2957              push @$open_elements, [$head_element, 'head'];              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2958              $insertion_mode = 'in head';              $self->{insertion_mode} = 'in head';
2959              ## reprocess              ## reprocess
2960              redo B;              redo B;
2961            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
2962              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
2963              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
2964              !!!next-token;              !!!next-token;
2965              redo B;              redo B;
2966            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
2967              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};
2968              !!!create-element ($head_element, 'head', $attr);              !!!create-element ($self->{head_element}, 'head', $attr);
2969              $open_elements->[-1]->[0]->append_child ($head_element);              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2970              push @$open_elements, [$head_element, 'head'];              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2971              $insertion_mode = 'in head';              $self->{insertion_mode} = 'in head';
2972              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
2973                !!!next-token;                !!!next-token;
2974              #} elsif ({              #} elsif ({
# Line 2927  sub _construct_tree ($) { Line 2983  sub _construct_tree ($) {
2983            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
2984              if ($token->{tag_name} eq 'html') {              if ($token->{tag_name} eq 'html') {
2985                ## As if <head>                ## As if <head>
2986                !!!create-element ($head_element, 'head');                !!!create-element ($self->{head_element}, 'head');
2987                $open_elements->[-1]->[0]->append_child ($head_element);                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2988                push @$open_elements, [$head_element, 'head'];                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];
2989                $insertion_mode = 'in head';                $self->{insertion_mode} = 'in head';
2990                ## reprocess                ## reprocess
2991                redo B;                redo B;
2992              } else {              } else {
2993                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
2994                ## Ignore the token                ## Ignore the token
2995                !!!next-token;                !!!next-token;
2996                redo B;                redo B;
# Line 2942  sub _construct_tree ($) { Line 2998  sub _construct_tree ($) {
2998            } else {            } else {
2999              die "$0: $token->{type}: Unknown type";              die "$0: $token->{type}: Unknown type";
3000            }            }
3001          } elsif ($insertion_mode eq 'in head') {          } elsif ($self->{insertion_mode} eq 'in head') {
3002            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3003              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3004                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3005                unless (length $token->{data}) {                unless (length $token->{data}) {
3006                  !!!next-token;                  !!!next-token;
3007                  redo B;                  redo B;
# Line 2955  sub _construct_tree ($) { Line 3011  sub _construct_tree ($) {
3011              #              #
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') {
# Line 2963  sub _construct_tree ($) { Line 3019  sub _construct_tree ($) {
3019                ## NOTE: There is an "as if in head" code clone                ## NOTE: There is an "as if in head" code clone
3020                my $title_el;                my $title_el;
3021                !!!create-element ($title_el, 'title', $token->{attributes});                !!!create-element ($title_el, 'title', $token->{attributes});
3022                (defined $head_element ? $head_element : $open_elements->[-1]->[0])                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
3023                  ->append_child ($title_el);                  ->append_child ($title_el);
3024                $self->{content_model_flag} = 'RCDATA';                $self->{content_model_flag} = 'RCDATA';
3025    
# Line 2983  sub _construct_tree ($) { Line 3039  sub _construct_tree ($) {
3039                    $token->{tag_name} eq 'title') {                    $token->{tag_name} eq 'title') {
3040                  ## Ignore the token                  ## Ignore the token
3041                } else {                } else {
3042                  !!!parse-error;                  !!!parse-error (type => 'in RCDATA:#'.$token->{type});
3043                  ## ISSUE: And ignore?                  ## ISSUE: And ignore?
3044                }                }
3045                !!!next-token;                !!!next-token;
# Line 2998  sub _construct_tree ($) { Line 3054  sub _construct_tree ($) {
3054                ## NOTE: There are "as if in head" code clones                ## NOTE: There are "as if in head" code clones
3055                my $el;                my $el;
3056                !!!create-element ($el, $token->{tag_name}, $token->{attributes});                !!!create-element ($el, $token->{tag_name}, $token->{attributes});
3057                (defined $head_element ? $head_element : $open_elements->[-1]->[0])                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])
3058                  ->append_child ($el);                  ->append_child ($el);
3059    
3060                !!!next-token;                !!!next-token;
3061                redo B;                redo B;
3062              } elsif ($token->{tag_name} eq 'head') {              } elsif ($token->{tag_name} eq 'head') {
3063                !!!parse-error;                !!!parse-error (type => 'in head:head');
3064                ## Ignore the token                ## Ignore the token
3065                !!!next-token;                !!!next-token;
3066                redo B;                redo B;
# Line 3013  sub _construct_tree ($) { Line 3069  sub _construct_tree ($) {
3069              }              }
3070            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
3071              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
3072                if ($open_elements->[-1]->[1] eq 'head') {                if ($self->{open_elements}->[-1]->[1] eq 'head') {
3073                  pop @$open_elements;                  pop @{$self->{open_elements}};
3074                } else {                } else {
3075                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:head');
3076                }                }
3077                $insertion_mode = 'after head';                $self->{insertion_mode} = 'after head';
3078                !!!next-token;                !!!next-token;
3079                redo B;                redo B;
3080              } elsif ($token->{tag_name} eq 'html') {              } elsif ($token->{tag_name} eq 'html') {
3081                #                #
3082              } else {              } else {
3083                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3084                ## Ignore the token                ## Ignore the token
3085                !!!next-token;                !!!next-token;
3086                redo B;                redo B;
# Line 3033  sub _construct_tree ($) { Line 3089  sub _construct_tree ($) {
3089              #              #
3090            }            }
3091    
3092            if ($open_elements->[-1]->[1] eq 'head') {            if ($self->{open_elements}->[-1]->[1] eq 'head') {
3093              ## As if </head>              ## As if </head>
3094              pop @$open_elements;              pop @{$self->{open_elements}};
3095            }            }
3096            $insertion_mode = 'after head';            $self->{insertion_mode} = 'after head';
3097            ## reprocess            ## reprocess
3098            redo B;            redo B;
3099    
3100            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
3101          } elsif ($insertion_mode eq 'after head') {          } elsif ($self->{insertion_mode} eq 'after head') {
3102            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3103              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3104                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3105                unless (length $token->{data}) {                unless (length $token->{data}) {
3106                  !!!next-token;                  !!!next-token;
3107                  redo B;                  redo B;
# Line 3055  sub _construct_tree ($) { Line 3111  sub _construct_tree ($) {
3111              #              #
3112            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3113              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3114              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3115              !!!next-token;              !!!next-token;
3116              redo B;              redo B;
3117            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
3118              if ($token->{tag_name} eq 'body') {              if ($token->{tag_name} eq 'body') {
3119                !!!insert-element ('body', $token->{attributes});                !!!insert-element ('body', $token->{attributes});
3120                $insertion_mode = 'in body';                $self->{insertion_mode} = 'in body';
3121                !!!next-token;                !!!next-token;
3122                redo B;                redo B;
3123              } elsif ($token->{tag_name} eq 'frameset') {              } elsif ($token->{tag_name} eq 'frameset') {
3124                !!!insert-element ('frameset', $token->{attributes});                !!!insert-element ('frameset', $token->{attributes});
3125                $insertion_mode = 'in frameset';                $self->{insertion_mode} = 'in frameset';
3126                !!!next-token;                !!!next-token;
3127                redo B;                redo B;
3128              } elsif ({              } elsif ({
3129                        base => 1, link => 1, meta => 1,                        base => 1, link => 1, meta => 1,
3130                        script=> 1, style => 1, title => 1,                        script => 1, style => 1, title => 1,
3131                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3132                !!!parse-error;                !!!parse-error (type => 'after head:'.$token->{tag_name});
3133                 $insertion_mode = 'in head';                $self->{insertion_mode} = 'in head';
3134                ## reprocess                ## reprocess
3135                redo B;                redo B;
3136              } else {              } else {
# Line 3086  sub _construct_tree ($) { Line 3142  sub _construct_tree ($) {
3142                        
3143            ## As if <body>            ## As if <body>
3144            !!!insert-element ('body');            !!!insert-element ('body');
3145            $insertion_mode = 'in body';            $self->{insertion_mode} = 'in body';
3146            ## reprocess            ## reprocess
3147            redo B;            redo B;
3148          } elsif ($insertion_mode eq 'in body') {          } elsif ($self->{insertion_mode} eq 'in body') {
3149            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3150              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
3151              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
3152                            
3153              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3154    
3155              !!!next-token;              !!!next-token;
3156              redo B;              redo B;
3157            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3158              ## NOTE: There is a code clone of "comment in body".              ## NOTE: There is a code clone of "comment in body".
3159              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3160              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3161              !!!next-token;              !!!next-token;
3162              redo B;              redo B;
3163            } else {            } else {
3164              $in_body->($insert_to_current);              $in_body->($insert_to_current);
3165              redo B;              redo B;
3166            }            }
3167          } elsif ($insertion_mode eq 'in table') {          } elsif ($self->{insertion_mode} eq 'in table') {
3168            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3169              ## NOTE: There are "character in table" code clones.              ## NOTE: There are "character in table" code clones.
3170              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3171                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3172                                
3173                unless (length $token->{data}) {                unless (length $token->{data}) {
3174                  !!!next-token;                  !!!next-token;
# Line 3120  sub _construct_tree ($) { Line 3176  sub _construct_tree ($) {
3176                }                }
3177              }              }
3178    
3179                !!!parse-error (type => 'in table:#character');
3180    
3181              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3182              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3183              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3129  sub _construct_tree ($) { Line 3187  sub _construct_tree ($) {
3187              if ({              if ({
3188                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3189                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3190                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3191                # MUST                # MUST
3192                my $foster_parent_element;                my $foster_parent_element;
3193                my $next_sibling;                my $next_sibling;
3194                my $prev_sibling;                my $prev_sibling;
3195                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3196                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3197                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3198                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3199                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3200                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3201                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
3202                    } else {                    } else {
3203                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3204                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
3205                    }                    }
3206                    last OE;                    last OE;
3207                  }                  }
3208                } # OE                } # OE
3209                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
3210                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
3211                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
3212                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3160  sub _construct_tree ($) { Line 3218  sub _construct_tree ($) {
3218                     $next_sibling);                     $next_sibling);
3219                }                }
3220              } else {              } else {
3221                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3222              }              }
3223                            
3224              !!!next-token;              !!!next-token;
3225              redo B;              redo B;
3226            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3227              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3228              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3229              !!!next-token;              !!!next-token;
3230              redo B;              redo B;
3231            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3177  sub _construct_tree ($) { Line 3235  sub _construct_tree ($) {
3235                   tbody => 1, tfoot => 1, thead => 1,                   tbody => 1, tfoot => 1, thead => 1,
3236                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3237                ## Clear back to table context                ## Clear back to table context
3238                while ($open_elements->[-1]->[1] ne 'table' and                while ($self->{open_elements}->[-1]->[1] ne 'table' and
3239                       $open_elements->[-1]->[1] ne 'html') {                       $self->{open_elements}->[-1]->[1] ne 'html') {
3240                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3241                  pop @$open_elements;                  pop @{$self->{open_elements}};
3242                }                }
3243    
3244                push @$active_formatting_elements, ['#marker', '']                push @$active_formatting_elements, ['#marker', '']
3245                  if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3246    
3247                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3248                $insertion_mode = {                $self->{insertion_mode} = {
3249                                   caption => 'in caption',                                   caption => 'in caption',
3250                                   colgroup => 'in column group',                                   colgroup => 'in column group',
3251                                   tbody => 'in table body',                                   tbody => 'in table body',
# Line 3201  sub _construct_tree ($) { Line 3259  sub _construct_tree ($) {
3259                        td => 1, th => 1, tr => 1,                        td => 1, th => 1, tr => 1,
3260                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3261                ## Clear back to table context                ## Clear back to table context
3262                while ($open_elements->[-1]->[1] ne 'table' and                while ($self->{open_elements}->[-1]->[1] ne 'table' and
3263                       $open_elements->[-1]->[1] ne 'html') {                       $self->{open_elements}->[-1]->[1] ne 'html') {
3264                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3265                  pop @$open_elements;                  pop @{$self->{open_elements}};
3266                }                }
3267    
3268                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');
3269                $insertion_mode = $token->{tag_name} eq 'col'                $self->{insertion_mode} = $token->{tag_name} eq 'col'
3270                  ? 'in column group' : 'in table body';                  ? 'in column group' : 'in table body';
3271                ## reprocess                ## reprocess
3272                redo B;                redo B;
3273              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3274                ## NOTE: There are code clones for this "table in table"                ## NOTE: There are code clones for this "table in table"
3275                !!!parse-error;                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3276    
3277                ## As if </table>                ## As if </table>
3278                ## have a table element in table scope                ## have a table element in table scope
3279                my $i;                my $i;
3280                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3281                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3282                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
3283                    $i = $_;                    $i = $_;
3284                    last INSCOPE;                    last INSCOPE;
# Line 3231  sub _construct_tree ($) { Line 3289  sub _construct_tree ($) {
3289                  }                  }
3290                } # INSCOPE                } # INSCOPE
3291                unless (defined $i) {                unless (defined $i) {
3292                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
3293                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
3294                  !!!next-token;                  !!!next-token;
3295                  redo B;                  redo B;
# Line 3241  sub _construct_tree ($) { Line 3299  sub _construct_tree ($) {
3299                if ({                if ({
3300                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3301                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3302                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3303                  !!!back-token; # <table>                  !!!back-token; # <table>
3304                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
3305                  !!!back-token;                  !!!back-token;
3306                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3307                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3308                  redo B;                  redo B;
3309                }                }
3310    
3311                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3312                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3313                }                }
3314    
3315                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3316    
3317                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
3318    
3319                ## reprocess                ## reprocess
3320                redo B;                redo B;
# Line 3267  sub _construct_tree ($) { Line 3325  sub _construct_tree ($) {
3325              if ($token->{tag_name} eq 'table') {              if ($token->{tag_name} eq 'table') {
3326                ## have a table element in table scope                ## have a table element in table scope
3327                my $i;                my $i;
3328                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3329                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3330                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3331                    $i = $_;                    $i = $_;
3332                    last INSCOPE;                    last INSCOPE;
# Line 3279  sub _construct_tree ($) { Line 3337  sub _construct_tree ($) {
3337                  }                  }
3338                } # INSCOPE                } # INSCOPE
3339                unless (defined $i) {                unless (defined $i) {
3340                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3341                  ## Ignore the token                  ## Ignore the token
3342                  !!!next-token;                  !!!next-token;
3343                  redo B;                  redo B;
# Line 3289  sub _construct_tree ($) { Line 3347  sub _construct_tree ($) {
3347                if ({                if ({
3348                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3349                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3350                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3351                  !!!back-token;                  !!!back-token;
3352                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3353                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3354                  redo B;                  redo B;
3355                }                }
3356    
3357                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3358                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3359                }                }
3360    
3361                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3362    
3363                $reset_insertion_mode->();                $self->_reset_insertion_mode;
3364    
3365                !!!next-token;                !!!next-token;
3366                redo B;                redo B;
# Line 3311  sub _construct_tree ($) { Line 3369  sub _construct_tree ($) {
3369                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,
3370                        thead => 1, tr => 1,                        thead => 1, tr => 1,
3371                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3372                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3373                ## Ignore the token                ## Ignore the token
3374                !!!next-token;                !!!next-token;
3375                redo B;                redo B;
# Line 3322  sub _construct_tree ($) { Line 3380  sub _construct_tree ($) {
3380              #              #
3381            }            }
3382    
3383            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
3384            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
3385            redo B;            redo B;
3386          } elsif ($insertion_mode eq 'in caption') {          } elsif ($self->{insertion_mode} eq 'in caption') {
3387            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3388              ## NOTE: This is a code clone of "character in body".              ## NOTE: This is a code clone of "character in body".
3389              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
3390                            
3391              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3392    
3393              !!!next-token;              !!!next-token;
3394              redo B;              redo B;
3395            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3396              ## NOTE: This is a code clone of "comment in body".              ## NOTE: This is a code clone of "comment in body".
3397              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3398              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3399              !!!next-token;              !!!next-token;
3400              redo B;              redo B;
3401            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3345  sub _construct_tree ($) { Line 3403  sub _construct_tree ($) {
3403                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
3404                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
3405                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3406                !!!parse-error;                !!!parse-error (type => 'not closed:caption');
3407    
3408                ## As if </caption>                ## As if </caption>
3409                ## have a table element in table scope                ## have a table element in table scope
3410                my $i;                my $i;
3411                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3412                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3413                  if ($node->[1] eq 'caption') {                  if ($node->[1] eq 'caption') {
3414                    $i = $_;                    $i = $_;
3415                    last INSCOPE;                    last INSCOPE;
# Line 3362  sub _construct_tree ($) { Line 3420  sub _construct_tree ($) {
3420                  }                  }
3421                } # INSCOPE                } # INSCOPE
3422                unless (defined $i) {                unless (defined $i) {
3423                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:caption');
3424                  ## Ignore the token                  ## Ignore the token
3425                  !!!next-token;                  !!!next-token;
3426                  redo B;                  redo B;
# Line 3372  sub _construct_tree ($) { Line 3430  sub _construct_tree ($) {
3430                if ({                if ({
3431                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3432                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3433                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3434                  !!!back-token; # <?>                  !!!back-token; # <?>
3435                  $token = {type => 'end tag', tag_name => 'caption'};                  $token = {type => 'end tag', tag_name => 'caption'};
3436                  !!!back-token;                  !!!back-token;
3437                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3438                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3439                  redo B;                  redo B;
3440                }                }
3441    
3442                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3443                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3444                }                }
3445    
3446                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3447    
3448                $clear_up_to_marker->();                $clear_up_to_marker->();
3449    
3450                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3451    
3452                ## reprocess                ## reprocess
3453                redo B;                redo B;
# Line 3400  sub _construct_tree ($) { Line 3458  sub _construct_tree ($) {
3458              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'caption') {
3459                ## have a table element in table scope                ## have a table element in table scope
3460                my $i;                my $i;
3461                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3462                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3463                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3464                    $i = $_;                    $i = $_;
3465                    last INSCOPE;                    last INSCOPE;
# Line 3412  sub _construct_tree ($) { Line 3470  sub _construct_tree ($) {
3470                  }                  }
3471                } # INSCOPE                } # INSCOPE
3472                unless (defined $i) {                unless (defined $i) {
3473                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3474                  ## Ignore the token                  ## Ignore the token
3475                  !!!next-token;                  !!!next-token;
3476                  redo B;                  redo B;
# Line 3422  sub _construct_tree ($) { Line 3480  sub _construct_tree ($) {
3480                if ({                if ({
3481                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3482                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3483                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3484                  !!!back-token;                  !!!back-token;
3485                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3486                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3487                  redo B;                  redo B;
3488                }                }
3489    
3490                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3491                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3492                }                }
3493    
3494                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3495    
3496                $clear_up_to_marker->();                $clear_up_to_marker->();
3497    
3498                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3499    
3500                !!!next-token;                !!!next-token;
3501                redo B;                redo B;
3502              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3503                !!!parse-error;                !!!parse-error (type => 'not closed:caption');
3504    
3505                ## As if </caption>                ## As if </caption>
3506                ## have a table element in table scope                ## have a table element in table scope
3507                my $i;                my $i;
3508                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3509                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3510                  if ($node->[1] eq 'caption') {                  if ($node->[1] eq 'caption') {
3511                    $i = $_;                    $i = $_;
3512                    last INSCOPE;                    last INSCOPE;
# Line 3459  sub _construct_tree ($) { Line 3517  sub _construct_tree ($) {
3517                  }                  }
3518                } # INSCOPE                } # INSCOPE
3519                unless (defined $i) {                unless (defined $i) {
3520                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:caption');
3521                  ## Ignore the token                  ## Ignore the token
3522                  !!!next-token;                  !!!next-token;
3523                  redo B;                  redo B;
# Line 3469  sub _construct_tree ($) { Line 3527  sub _construct_tree ($) {
3527                if ({                if ({
3528                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3529                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3530                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3531                  !!!back-token; # </table>                  !!!back-token; # </table>
3532                  $token = {type => 'end tag', tag_name => 'caption'};                  $token = {type => 'end tag', tag_name => 'caption'};
3533                  !!!back-token;                  !!!back-token;
3534                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3535                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3536                  redo B;                  redo B;
3537                }                }
3538    
3539                if ($open_elements->[-1]->[1] ne 'caption') {                if ($self->{open_elements}->[-1]->[1] ne 'caption') {
3540                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3541                }                }
3542    
3543                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3544    
3545                $clear_up_to_marker->();                $clear_up_to_marker->();
3546    
3547                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3548    
3549                ## reprocess                ## reprocess
3550                redo B;                redo B;
# Line 3495  sub _construct_tree ($) { Line 3553  sub _construct_tree ($) {
3553                        html => 1, tbody => 1, td => 1, tfoot => 1,                        html => 1, tbody => 1, td => 1, tfoot => 1,
3554                        th => 1, thead => 1, tr => 1,                        th => 1, thead => 1, tr => 1,
3555                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3556                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3557                ## Ignore the token                ## Ignore the token
3558                redo B;                redo B;
3559              } else {              } else {
# Line 3507  sub _construct_tree ($) { Line 3565  sub _construct_tree ($) {
3565                                
3566            $in_body->($insert_to_current);            $in_body->($insert_to_current);
3567            redo B;            redo B;
3568          } elsif ($insertion_mode eq 'in column group') {          } elsif ($self->{insertion_mode} eq 'in column group') {
3569            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3570              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3571                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3572                unless (length $token->{data}) {                unless (length $token->{data}) {
3573                  !!!next-token;                  !!!next-token;
3574                  redo B;                  redo B;
# Line 3520  sub _construct_tree ($) { Line 3578  sub _construct_tree ($) {
3578              #              #
3579            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3580              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3581              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3582              !!!next-token;              !!!next-token;
3583              redo B;              redo B;
3584            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
3585              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
3586                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3587                pop @$open_elements;                pop @{$self->{open_elements}};
3588                !!!next-token;                !!!next-token;
3589                redo B;                redo B;
3590              } else {              } else {
# Line 3534  sub _construct_tree ($) { Line 3592  sub _construct_tree ($) {
3592              }              }
3593            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
3594              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3595                if ($open_elements->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] eq 'html') {
3596                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:colgroup');
3597                  ## Ignore the token                  ## Ignore the token
3598                  !!!next-token;                  !!!next-token;
3599                  redo B;                  redo B;
3600                } else {                } else {
3601                  pop @$open_elements; # colgroup                  pop @{$self->{open_elements}}; # colgroup
3602                  $insertion_mode = 'in table';                  $self->{insertion_mode} = 'in table';
3603                  !!!next-token;                  !!!next-token;
3604                  redo B;                              redo B;            
3605                }                }
3606              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
3607                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:col');
3608                ## Ignore the token                ## Ignore the token
3609                !!!next-token;                !!!next-token;
3610                redo B;                redo B;
# Line 3558  sub _construct_tree ($) { Line 3616  sub _construct_tree ($) {
3616            }            }
3617    
3618            ## As if </colgroup>            ## As if </colgroup>
3619            if ($open_elements->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] eq 'html') {
3620              !!!parse-error;              !!!parse-error (type => 'unmatched end tag:colgroup');
3621              ## Ignore the token              ## Ignore the token
3622              !!!next-token;              !!!next-token;
3623              redo B;              redo B;
3624            } else {            } else {
3625              pop @$open_elements; # colgroup              pop @{$self->{open_elements}}; # colgroup
3626              $insertion_mode = 'in table';              $self->{insertion_mode} = 'in table';
3627              ## reprocess              ## reprocess
3628              redo B;              redo B;
3629            }            }
3630          } elsif ($insertion_mode eq 'in table body') {          } elsif ($self->{insertion_mode} eq 'in table body') {
3631            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3632              ## NOTE: This is a "character in table" code clone.              ## NOTE: This is a "character in table" code clone.
3633              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3634                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3635                                
3636                unless (length $token->{data}) {                unless (length $token->{data}) {
3637                  !!!next-token;                  !!!next-token;
# Line 3581  sub _construct_tree ($) { Line 3639  sub _construct_tree ($) {
3639                }                }
3640              }              }
3641    
3642                !!!parse-error (type => 'in table:#character');
3643    
3644              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3645              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3646              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3590  sub _construct_tree ($) { Line 3650  sub _construct_tree ($) {
3650              if ({              if ({
3651                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3652                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3653                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3654                # MUST                # MUST
3655                my $foster_parent_element;                my $foster_parent_element;
3656                my $next_sibling;                my $next_sibling;
3657                my $prev_sibling;                my $prev_sibling;
3658                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3659                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3660                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3661                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3662                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3663                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3664                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
3665                    } else {                    } else {
3666                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3667                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
3668                    }                    }
3669                    last OE;                    last OE;
3670                  }                  }
3671                } # OE                } # OE
3672                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
3673                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
3674                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
3675                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3621  sub _construct_tree ($) { Line 3681  sub _construct_tree ($) {
3681                     $next_sibling);                     $next_sibling);
3682                }                }
3683              } else {              } else {
3684                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3685              }              }
3686                            
3687              !!!next-token;              !!!next-token;
# Line 3629  sub _construct_tree ($) { Line 3689  sub _construct_tree ($) {
3689            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3690              ## Copied from 'in table'              ## Copied from 'in table'
3691              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3692              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3693              !!!next-token;              !!!next-token;
3694              redo B;              redo B;
3695            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3637  sub _construct_tree ($) { Line 3697  sub _construct_tree ($) {
3697                   tr => 1,                   tr => 1,
3698                   th => 1, td => 1,                   th => 1, td => 1,
3699                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3700                  unless ($token->{tag_name} eq 'tr') {
3701                    !!!parse-error (type => 'missing start tag:tr');
3702                  }
3703    
3704                ## Clear back to table body context                ## Clear back to table body context
3705                while (not {                while (not {
3706                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3707                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3708                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3709                  pop @$open_elements;                  pop @{$self->{open_elements}};
3710                }                }
3711                                
3712                $insertion_mode = 'in row';                $self->{insertion_mode} = 'in row';
3713                if ($token->{tag_name} eq 'tr') {                if ($token->{tag_name} eq 'tr') {
3714                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes});
3715                  !!!next-token;                  !!!next-token;
# Line 3660  sub _construct_tree ($) { Line 3724  sub _construct_tree ($) {
3724                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3725                ## have an element in table scope                ## have an element in table scope
3726                my $i;                my $i;
3727                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3728                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3729                  if ({                  if ({
3730                       tbody => 1, thead => 1, tfoot => 1,                       tbody => 1, thead => 1, tfoot => 1,
3731                      }->{$node->[1]}) {                      }->{$node->[1]}) {
# Line 3674  sub _construct_tree ($) { Line 3738  sub _construct_tree ($) {
3738                  }                  }
3739                } # INSCOPE                } # INSCOPE
3740                unless (defined $i) {                unless (defined $i) {
3741                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3742                  ## Ignore the token                  ## Ignore the token
3743                  !!!next-token;                  !!!next-token;
3744                  redo B;                  redo B;
# Line 3683  sub _construct_tree ($) { Line 3747  sub _construct_tree ($) {
3747                ## Clear back to table body context                ## Clear back to table body context
3748                while (not {                while (not {
3749                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3750                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3751                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3752                  pop @$open_elements;                  pop @{$self->{open_elements}};
3753                }                }
3754    
3755                ## As if <{current node}>                ## As if <{current node}>
# Line 3695  sub _construct_tree ($) { Line 3759  sub _construct_tree ($) {
3759                ## Clear back to table body context                ## Clear back to table body context
3760                ## nop by definition                ## nop by definition
3761    
3762                pop @$open_elements;                pop @{$self->{open_elements}};
3763                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3764                ## reprocess                ## reprocess
3765                redo B;                redo B;
3766              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3767                ## NOTE: This is a code clone of "table in table"                ## NOTE: This is a code clone of "table in table"
3768                !!!parse-error;                !!!parse-error (type => 'not closed:table');
3769    
3770                ## As if </table>                ## As if </table>
3771                ## have a table element in table scope                ## have a table element in table scope
3772                my $i;                my $i;
3773                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3774                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3775                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
3776                    $i = $_;                    $i = $_;
3777                    last INSCOPE;                    last INSCOPE;
# Line 3718  sub _construct_tree ($) { Line 3782  sub _construct_tree ($) {
3782                  }                  }
3783                } # INSCOPE                } # INSCOPE
3784                unless (defined $i) {                unless (defined $i) {
3785                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
3786                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
3787                  !!!next-token;                  !!!next-token;
3788                  redo B;                  redo B;
# Line 3728  sub _construct_tree ($) { Line 3792  sub _construct_tree ($) {
3792                if ({                if ({
3793                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
3794                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
3795                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
3796                  !!!back-token; # <table>                  !!!back-token; # <table>
3797                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
3798                  !!!back-token;                  !!!back-token;
3799                  $token = {type => 'end tag',                  $token = {type => 'end tag',
3800                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
3801                  redo B;                  redo B;
3802                }                }
3803    
3804                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
3805                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3806                }                }
3807    
3808                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
3809    
3810                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
3811    
3812                ## reprocess                ## reprocess
3813                redo B;                redo B;
# Line 3756  sub _construct_tree ($) { Line 3820  sub _construct_tree ($) {
3820                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
3821                ## have an element in table scope                ## have an element in table scope
3822                my $i;                my $i;
3823                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3824                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3825                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
3826                    $i = $_;                    $i = $_;
3827                    last INSCOPE;                    last INSCOPE;
# Line 3768  sub _construct_tree ($) { Line 3832  sub _construct_tree ($) {
3832                  }                  }
3833                } # INSCOPE                } # INSCOPE
3834                unless (defined $i) {                unless (defined $i) {
3835                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3836                  ## Ignore the token                  ## Ignore the token
3837                  !!!next-token;                  !!!next-token;
3838                  redo B;                  redo B;
# Line 3777  sub _construct_tree ($) { Line 3841  sub _construct_tree ($) {
3841                ## Clear back to table body context                ## Clear back to table body context
3842                while (not {                while (not {
3843                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3844                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3845                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3846                  pop @$open_elements;                  pop @{$self->{open_elements}};
3847                }                }
3848    
3849                pop @$open_elements;                pop @{$self->{open_elements}};
3850                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3851                !!!next-token;                !!!next-token;
3852                redo B;                redo B;
3853              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3854                ## have an element in table scope                ## have an element in table scope
3855                my $i;                my $i;
3856                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3857                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
3858                  if ({                  if ({
3859                       tbody => 1, thead => 1, tfoot => 1,                       tbody => 1, thead => 1, tfoot => 1,
3860                      }->{$node->[1]}) {                      }->{$node->[1]}) {
# Line 3803  sub _construct_tree ($) { Line 3867  sub _construct_tree ($) {
3867                  }                  }
3868                } # INSCOPE                } # INSCOPE
3869                unless (defined $i) {                unless (defined $i) {
3870                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3871                  ## Ignore the token                  ## Ignore the token
3872                  !!!next-token;                  !!!next-token;
3873                  redo B;                  redo B;
# Line 3812  sub _construct_tree ($) { Line 3876  sub _construct_tree ($) {
3876                ## Clear back to table body context                ## Clear back to table body context
3877                while (not {                while (not {
3878                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  tbody => 1, tfoot => 1, thead => 1, html => 1,
3879                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3880                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3881                  pop @$open_elements;                  pop @{$self->{open_elements}};
3882                }                }
3883    
3884                ## As if <{current node}>                ## As if <{current node}>
# Line 3824  sub _construct_tree ($) { Line 3888  sub _construct_tree ($) {
3888                ## Clear back to table body context                ## Clear back to table body context
3889                ## nop by definition                ## nop by definition
3890    
3891                pop @$open_elements;                pop @{$self->{open_elements}};
3892                $insertion_mode = 'in table';                $self->{insertion_mode} = 'in table';
3893                ## reprocess                ## reprocess
3894                redo B;                redo B;
3895              } elsif ({              } elsif ({
3896                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
3897                        html => 1, td => 1, th => 1, tr => 1,                        html => 1, td => 1, th => 1, tr => 1,
3898                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
3899                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
3900                ## Ignore the token                ## Ignore the token
3901                !!!next-token;                !!!next-token;
3902                redo B;                redo B;
# Line 3844  sub _construct_tree ($) { Line 3908  sub _construct_tree ($) {
3908            }            }
3909                        
3910            ## As if in table            ## As if in table
3911            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
3912            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
3913            redo B;            redo B;
3914          } elsif ($insertion_mode eq 'in row') {          } elsif ($self->{insertion_mode} eq 'in row') {
3915            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3916              ## NOTE: This is a "character in table" code clone.              ## NOTE: This is a "character in table" code clone.
3917              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
3918                $open_elements->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
3919                                
3920                unless (length $token->{data}) {                unless (length $token->{data}) {
3921                  !!!next-token;                  !!!next-token;
# Line 3859  sub _construct_tree ($) { Line 3923  sub _construct_tree ($) {
3923                }                }
3924              }              }
3925    
3926                !!!parse-error (type => 'in table:#character');
3927    
3928              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
3929              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
3930              ## into the current node" while characters might not be              ## into the current node" while characters might not be
# Line 3868  sub _construct_tree ($) { Line 3934  sub _construct_tree ($) {
3934              if ({              if ({
3935                   table => 1, tbody => 1, tfoot => 1,                   table => 1, tbody => 1, tfoot => 1,
3936                   thead => 1, tr => 1,                   thead => 1, tr => 1,
3937                  }->{$open_elements->[-1]->[1]}) {                  }->{$self->{open_elements}->[-1]->[1]}) {
3938                # MUST                # MUST
3939                my $foster_parent_element;                my $foster_parent_element;
3940                my $next_sibling;                my $next_sibling;
3941                my $prev_sibling;                my $prev_sibling;
3942                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
3943                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] eq 'table') {
3944                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3945                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
3946                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
3947                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
3948                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
3949                    } else {                    } else {
3950                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3951                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
3952                    }                    }
3953                    last OE;                    last OE;
3954                  }                  }
3955                } # OE                } # OE
3956                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
3957                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
3958                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
3959                if (defined $prev_sibling and                if (defined $prev_sibling and
# Line 3899  sub _construct_tree ($) { Line 3965  sub _construct_tree ($) {
3965                     $next_sibling);                     $next_sibling);
3966                }                }
3967              } else {              } else {
3968                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
3969              }              }
3970                            
3971              !!!next-token;              !!!next-token;
# Line 3907  sub _construct_tree ($) { Line 3973  sub _construct_tree ($) {
3973            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
3974              ## Copied from 'in table'              ## Copied from 'in table'
3975              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
3976              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
3977              !!!next-token;              !!!next-token;
3978              redo B;              redo B;
3979            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 3916  sub _construct_tree ($) { Line 3982  sub _construct_tree ($) {
3982                ## Clear back to table row context                ## Clear back to table row context
3983                while (not {                while (not {
3984                  tr => 1, html => 1,                  tr => 1, html => 1,
3985                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
3986                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
3987                  pop @$open_elements;                  pop @{$self->{open_elements}};
3988                }                }
3989                                
3990                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
3991                $insertion_mode = 'in cell';                $self->{insertion_mode} = 'in cell';
3992    
3993                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
3994                                
# Line 3935  sub _construct_tree ($) { Line 4001  sub _construct_tree ($) {
4001                ## As if </tr>                ## As if </tr>
4002                ## have an element in table scope                ## have an element in table scope
4003                my $i;                my $i;
4004                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4005                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4006                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4007                    $i = $_;                    $i = $_;
4008                    last INSCOPE;                    last INSCOPE;
# Line 3947  sub _construct_tree ($) { Line 4013  sub _construct_tree ($) {
4013                  }                  }
4014                } # INSCOPE                } # INSCOPE
4015                unless (defined $i) {                unless (defined $i) {
4016                  !!!parse-error;                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});
4017                  ## Ignore the token                  ## Ignore the token
4018                  !!!next-token;                  !!!next-token;
4019                  redo B;                  redo B;
# Line 3956  sub _construct_tree ($) { Line 4022  sub _construct_tree ($) {
4022                ## Clear back to table row context                ## Clear back to table row context
4023                while (not {                while (not {
4024                  tr => 1, html => 1,                  tr => 1, html => 1,
4025                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4026                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4027                  pop @$open_elements;                  pop @{$self->{open_elements}};
4028                }                }
4029    
4030                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4031                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4032                ## reprocess                ## reprocess
4033                redo B;                redo B;
4034              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4035                ## NOTE: This is a code clone of "table in table"                ## NOTE: This is a code clone of "table in table"
4036                !!!parse-error;                !!!parse-error (type => 'not closed:table');
4037    
4038                ## As if </table>                ## As if </table>
4039                ## have a table element in table scope                ## have a table element in table scope
4040                my $i;                my $i;
4041                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4042                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4043                  if ($node->[1] eq 'table') {                  if ($node->[1] eq 'table') {
4044                    $i = $_;                    $i = $_;
4045                    last INSCOPE;                    last INSCOPE;
# Line 3984  sub _construct_tree ($) { Line 4050  sub _construct_tree ($) {
4050                  }                  }
4051                } # INSCOPE                } # INSCOPE
4052                unless (defined $i) {                unless (defined $i) {
4053                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:table');
4054                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
4055                  !!!next-token;                  !!!next-token;
4056                  redo B;                  redo B;
# Line 3994  sub _construct_tree ($) { Line 4060  sub _construct_tree ($) {
4060                if ({                if ({
4061                     dd => 1, dt => 1, li => 1, p => 1,                     dd => 1, dt => 1, li => 1, p => 1,
4062                     td => 1, th => 1, tr => 1,                     td => 1, th => 1, tr => 1,
4063                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
4064                  !!!back-token; # <table>                  !!!back-token; # <table>
4065                  $token = {type => 'end tag', tag_name => 'table'};                  $token = {type => 'end tag', tag_name => 'table'};
4066                  !!!back-token;                  !!!back-token;
4067                  $token = {type => 'end tag',                  $token = {type => 'end tag',
4068                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4069                  redo B;                  redo B;
4070                }                }
4071    
4072                if ($open_elements->[-1]->[1] ne 'table') {                if ($self->{open_elements}->[-1]->[1] ne 'table') {
4073                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4074                }                }
4075    
4076                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4077    
4078                $reset_insertion_mode->();                              $self->_reset_insertion_mode;
4079    
4080                ## reprocess                ## reprocess
4081                redo B;                redo B;
# Line 4020  sub _construct_tree ($) { Line 4086  sub _construct_tree ($) {
4086              if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
4087                ## have an element in table scope                ## have an element in table scope
4088                my $i;                my $i;
4089                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4090                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4091                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4092                    $i = $_;                    $i = $_;
4093                    last INSCOPE;                    last INSCOPE;
# Line 4032  sub _construct_tree ($) { Line 4098  sub _construct_tree ($) {
4098                  }                  }
4099                } # INSCOPE                } # INSCOPE
4100                unless (defined $i) {                unless (defined $i) {
4101                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4102                  ## Ignore the token                  ## Ignore the token
4103                  !!!next-token;                  !!!next-token;
4104                  redo B;                  redo B;
# Line 4041  sub _construct_tree ($) { Line 4107  sub _construct_tree ($) {
4107                ## Clear back to table row context                ## Clear back to table row context
4108                while (not {                while (not {
4109                  tr => 1, html => 1,                  tr => 1, html => 1,
4110                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4111                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4112                  pop @$open_elements;                  pop @{$self->{open_elements}};
4113                }                }
4114    
4115                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4116                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4117                !!!next-token;                !!!next-token;
4118                redo B;                redo B;
4119              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
4120                ## As if </tr>                ## As if </tr>
4121                ## have an element in table scope                ## have an element in table scope
4122                my $i;                my $i;
4123                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4124                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4125                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4126                    $i = $_;                    $i = $_;
4127                    last INSCOPE;                    last INSCOPE;
# Line 4066  sub _construct_tree ($) { Line 4132  sub _construct_tree ($) {
4132                  }                  }
4133                } # INSCOPE                } # INSCOPE
4134                unless (defined $i) {                unless (defined $i) {
4135                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});
4136                  ## Ignore the token                  ## Ignore the token
4137                  !!!next-token;                  !!!next-token;
4138                  redo B;                  redo B;
# Line 4075  sub _construct_tree ($) { Line 4141  sub _construct_tree ($) {
4141                ## Clear back to table row context                ## Clear back to table row context
4142                while (not {                while (not {
4143                  tr => 1, html => 1,                  tr => 1, html => 1,
4144                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4145                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4146                  pop @$open_elements;                  pop @{$self->{open_elements}};
4147                }                }
4148    
4149                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4150                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4151                ## reprocess                ## reprocess
4152                redo B;                redo B;
4153              } elsif ({              } elsif ({
# Line 4089  sub _construct_tree ($) { Line 4155  sub _construct_tree ($) {
4155                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4156                ## have an element in table scope                ## have an element in table scope
4157                my $i;                my $i;
4158                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4159                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4160                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4161                    $i = $_;                    $i = $_;
4162                    last INSCOPE;                    last INSCOPE;
# Line 4101  sub _construct_tree ($) { Line 4167  sub _construct_tree ($) {
4167                  }                  }
4168                } # INSCOPE                } # INSCOPE
4169                unless (defined $i) {                unless (defined $i) {
4170                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4171                  ## Ignore the token                  ## Ignore the token
4172                  !!!next-token;                  !!!next-token;
4173                  redo B;                  redo B;
# Line 4110  sub _construct_tree ($) { Line 4176  sub _construct_tree ($) {
4176                ## As if </tr>                ## As if </tr>
4177                ## have an element in table scope                ## have an element in table scope
4178                my $i;                my $i;
4179                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4180                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4181                  if ($node->[1] eq 'tr') {                  if ($node->[1] eq 'tr') {
4182                    $i = $_;                    $i = $_;
4183                    last INSCOPE;                    last INSCOPE;
# Line 4122  sub _construct_tree ($) { Line 4188  sub _construct_tree ($) {
4188                  }                  }
4189                } # INSCOPE                } # INSCOPE
4190                unless (defined $i) {                unless (defined $i) {
4191                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:tr');
4192                  ## Ignore the token                  ## Ignore the token
4193                  !!!next-token;                  !!!next-token;
4194                  redo B;                  redo B;
# Line 4131  sub _construct_tree ($) { Line 4197  sub _construct_tree ($) {
4197                ## Clear back to table row context                ## Clear back to table row context
4198                while (not {                while (not {
4199                  tr => 1, html => 1,                  tr => 1, html => 1,
4200                }->{$open_elements->[-1]->[1]}) {                }->{$self->{open_elements}->[-1]->[1]}) {
4201                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4202                  pop @$open_elements;                  pop @{$self->{open_elements}};
4203                }                }
4204    
4205                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
4206                $insertion_mode = 'in table body';                $self->{insertion_mode} = 'in table body';
4207                ## reprocess                ## reprocess
4208                redo B;                redo B;
4209              } elsif ({              } elsif ({
4210                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1,
4211                        colgroup => 1, html => 1, td => 1, th => 1,                        colgroup => 1, html => 1, td => 1, th => 1,
4212                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4213                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4214                ## Ignore the token                ## Ignore the token
4215                !!!next-token;                !!!next-token;
4216                redo B;                redo B;
# Line 4156  sub _construct_tree ($) { Line 4222  sub _construct_tree ($) {
4222            }            }
4223    
4224            ## As if in table            ## As if in table
4225            !!!parse-error;            !!!parse-error (type => 'in table:'.$token->{tag_name});
4226            $in_body->($insert_to_foster);            $in_body->($insert_to_foster);
4227            redo B;            redo B;
4228          } elsif ($insertion_mode eq 'in cell') {          } elsif ($self->{insertion_mode} eq 'in cell') {
4229            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4230              ## NOTE: This is a code clone of "character in body".              ## NOTE: This is a code clone of "character in body".
4231              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
4232                            
4233              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4234    
4235              !!!next-token;              !!!next-token;
4236              redo B;              redo B;
4237            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4238              ## NOTE: This is a code clone of "comment in body".              ## NOTE: This is a code clone of "comment in body".
4239              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4240              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4241              !!!next-token;              !!!next-token;
4242              redo B;              redo B;
4243            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4182  sub _construct_tree ($) { Line 4248  sub _construct_tree ($) {
4248                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4249                ## have an element in table scope                ## have an element in table scope
4250                my $tn;                my $tn;
4251                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4252                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4253                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {
4254                    $tn = $node->[1];                    $tn = $node->[1];
4255                    last INSCOPE;                    last INSCOPE;
# Line 4194  sub _construct_tree ($) { Line 4260  sub _construct_tree ($) {
4260                  }                  }
4261                } # INSCOPE                } # INSCOPE
4262                unless (defined $tn) {                unless (defined $tn) {
4263                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4264                  ## Ignore the token                  ## Ignore the token
4265                  !!!next-token;                  !!!next-token;
4266                  redo B;                  redo B;
# Line 4211  sub _construct_tree ($) { Line 4277  sub _construct_tree ($) {
4277              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
4278                ## have an element in table scope                ## have an element in table scope
4279                my $i;                my $i;
4280                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4281                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4282                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4283                    $i = $_;                    $i = $_;
4284                    last INSCOPE;                    last INSCOPE;
# Line 4223  sub _construct_tree ($) { Line 4289  sub _construct_tree ($) {
4289                  }                  }
4290                } # INSCOPE                } # INSCOPE
4291                unless (defined $i) {                unless (defined $i) {
4292                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4293                  ## Ignore the token                  ## Ignore the token
4294                  !!!next-token;                  !!!next-token;
4295                  redo B;                  redo B;
# Line 4235  sub _construct_tree ($) { Line 4301  sub _construct_tree ($) {
4301                     td => ($token->{tag_name} eq 'th'),                     td => ($token->{tag_name} eq 'th'),
4302                     th => ($token->{tag_name} eq 'td'),                     th => ($token->{tag_name} eq 'td'),
4303                     tr => 1,                     tr => 1,
4304                    }->{$open_elements->[-1]->[1]}) {                    }->{$self->{open_elements}->[-1]->[1]}) {
4305                  !!!back-token;                  !!!back-token;
4306                  $token = {type => 'end tag',                  $token = {type => 'end tag',
4307                            tag_name => $open_elements->[-1]->[1]}; # MUST                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4308                  redo B;                  redo B;
4309                }                }
4310    
4311                if ($open_elements->[-1]->[1] ne $token->{tag_name}) {                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
4312                  !!!parse-error;                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4313                }                }
4314    
4315                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4316    
4317                $clear_up_to_marker->();                $clear_up_to_marker->();
4318    
4319                $insertion_mode = 'in row';                $self->{insertion_mode} = 'in row';
4320    
4321                !!!next-token;                !!!next-token;
4322                redo B;                redo B;
# Line 4258  sub _construct_tree ($) { Line 4324  sub _construct_tree ($) {
4324                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1,
4325                        colgroup => 1, html => 1,                        colgroup => 1, html => 1,
4326                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4327                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4328                ## Ignore the token                ## Ignore the token
4329                !!!next-token;                !!!next-token;
4330                redo B;                redo B;
# Line 4269  sub _construct_tree ($) { Line 4335  sub _construct_tree ($) {
4335                ## have an element in table scope                ## have an element in table scope
4336                my $i;                my $i;
4337                my $tn;                my $tn;
4338                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4339                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4340                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4341                    $i = $_;                    $i = $_;
4342                    last INSCOPE;                    last INSCOPE;
# Line 4285  sub _construct_tree ($) { Line 4351  sub _construct_tree ($) {
4351                  }                  }
4352                } # INSCOPE                } # INSCOPE
4353                unless (defined $i) {                unless (defined $i) {
4354                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4355                  ## Ignore the token                  ## Ignore the token
4356                  !!!next-token;                  !!!next-token;
4357                  redo B;                  redo B;
# Line 4304  sub _construct_tree ($) { Line 4370  sub _construct_tree ($) {
4370                        
4371            $in_body->($insert_to_current);            $in_body->($insert_to_current);
4372            redo B;            redo B;
4373          } elsif ($insertion_mode eq 'in select') {          } elsif ($self->{insertion_mode} eq 'in select') {
4374            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4375              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4376              !!!next-token;              !!!next-token;
4377              redo B;              redo B;
4378            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4379              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4380              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4381              !!!next-token;              !!!next-token;
4382              redo B;              redo B;
4383            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
4384              if ($token->{tag_name} eq 'option') {              if ($token->{tag_name} eq 'option') {
4385                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4386                  ## As if </option>                  ## As if </option>
4387                  pop @$open_elements;                  pop @{$self->{open_elements}};
4388                }                }
4389    
4390                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4391                !!!next-token;                !!!next-token;
4392                redo B;                redo B;
4393              } elsif ($token->{tag_name} eq 'optgroup') {              } elsif ($token->{tag_name} eq 'optgroup') {
4394                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4395                  ## As if </option>                  ## As if </option>
4396                  pop @$open_elements;                  pop @{$self->{open_elements}};
4397                }                }
4398    
4399                if ($open_elements->[-1]->[1] eq 'optgroup') {                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
4400                  ## As if </optgroup>                  ## As if </optgroup>
4401                  pop @$open_elements;                  pop @{$self->{open_elements}};
4402                }                }
4403    
4404                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4405                !!!next-token;                !!!next-token;
4406                redo B;                redo B;
4407              } elsif ($token->{tag_name} eq 'select') {              } elsif ($token->{tag_name} eq 'select') {
4408                !!!parse-error;                !!!parse-error (type => 'not closed:select');
4409                ## As if </select> instead                ## As if </select> instead
4410                ## have an element in table scope                ## have an element in table scope
4411                my $i;                my $i;
4412                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4413                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4414                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4415                    $i = $_;                    $i = $_;
4416                    last INSCOPE;                    last INSCOPE;
# Line 4355  sub _construct_tree ($) { Line 4421  sub _construct_tree ($) {
4421                  }                  }
4422                } # INSCOPE                } # INSCOPE
4423                unless (defined $i) {                unless (defined $i) {
4424                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:select');
4425                  ## Ignore the token                  ## Ignore the token
4426                  !!!next-token;                  !!!next-token;
4427                  redo B;                  redo B;
4428                }                }
4429                                
4430                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4431    
4432                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4433    
4434                !!!next-token;                !!!next-token;
4435                redo B;                redo B;
# Line 4372  sub _construct_tree ($) { Line 4438  sub _construct_tree ($) {
4438              }              }
4439            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4440              if ($token->{tag_name} eq 'optgroup') {              if ($token->{tag_name} eq 'optgroup') {
4441                if ($open_elements->[-1]->[1] eq 'option' and                if ($self->{open_elements}->[-1]->[1] eq 'option' and
4442                    $open_elements->[-2]->[1] eq 'optgroup') {                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {
4443                  ## As if </option>                  ## As if </option>
4444                  splice @$open_elements, -2;                  splice @{$self->{open_elements}}, -2;
4445                } elsif ($open_elements->[-1]->[1] eq 'optgroup') {                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {
4446                  pop @$open_elements;                  pop @{$self->{open_elements}};
4447                } else {                } else {
4448                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4449                  ## Ignore the token                  ## Ignore the token
4450                }                }
4451                !!!next-token;                !!!next-token;
4452                redo B;                redo B;
4453              } elsif ($token->{tag_name} eq 'option') {              } elsif ($token->{tag_name} eq 'option') {
4454                if ($open_elements->[-1]->[1] eq 'option') {                if ($self->{open_elements}->[-1]->[1] eq 'option') {
4455                  pop @$open_elements;                  pop @{$self->{open_elements}};
4456                } else {                } else {
4457                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4458                  ## Ignore the token                  ## Ignore the token
4459                }                }
4460                !!!next-token;                !!!next-token;
# Line 4396  sub _construct_tree ($) { Line 4462  sub _construct_tree ($) {
4462              } elsif ($token->{tag_name} eq 'select') {              } elsif ($token->{tag_name} eq 'select') {
4463                ## have an element in table scope                ## have an element in table scope
4464                my $i;                my $i;
4465                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4466                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4467                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4468                    $i = $_;                    $i = $_;
4469                    last INSCOPE;                    last INSCOPE;
# Line 4408  sub _construct_tree ($) { Line 4474  sub _construct_tree ($) {
4474                  }                  }
4475                } # INSCOPE                } # INSCOPE
4476                unless (defined $i) {                unless (defined $i) {
4477                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4478                  ## Ignore the token                  ## Ignore the token
4479                  !!!next-token;                  !!!next-token;
4480                  redo B;                  redo B;
4481                }                }
4482                                
4483                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4484    
4485                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4486    
4487                !!!next-token;                !!!next-token;
4488                redo B;                redo B;
# Line 4424  sub _construct_tree ($) { Line 4490  sub _construct_tree ($) {
4490                        caption => 1, table => 1, tbody => 1,                        caption => 1, table => 1, tbody => 1,
4491                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
4492                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4493                !!!parse-error;                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4494                                
4495                ## have an element in table scope                ## have an element in table scope
4496                my $i;                my $i;
4497                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4498                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4499                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] eq $token->{tag_name}) {
4500                    $i = $_;                    $i = $_;
4501                    last INSCOPE;                    last INSCOPE;
# Line 4448  sub _construct_tree ($) { Line 4514  sub _construct_tree ($) {
4514                ## As if </select>                ## As if </select>
4515                ## have an element in table scope                ## have an element in table scope
4516                undef $i;                undef $i;
4517                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4518                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
4519                  if ($node->[1] eq 'select') {                  if ($node->[1] eq 'select') {
4520                    $i = $_;                    $i = $_;
4521                    last INSCOPE;                    last INSCOPE;
# Line 4460  sub _construct_tree ($) { Line 4526  sub _construct_tree ($) {
4526                  }                  }
4527                } # INSCOPE                } # INSCOPE
4528                unless (defined $i) {                unless (defined $i) {
4529                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:select');
4530                  ## Ignore the </select> token                  ## Ignore the </select> token
4531                  !!!next-token; ## TODO: ok?                  !!!next-token; ## TODO: ok?
4532                  redo B;                  redo B;
4533                }                }
4534                                
4535                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
4536    
4537                $reset_insertion_mode->();                $self->_reset_insertion_mode;
4538    
4539                ## reprocess                ## reprocess
4540                redo B;                redo B;
# Line 4479  sub _construct_tree ($) { Line 4545  sub _construct_tree ($) {
4545              #              #
4546            }            }
4547    
4548            !!!parse-error;            !!!parse-error (type => 'in select:'.$token->{tag_name});
4549            ## Ignore the token            ## Ignore the token
4550            !!!next-token;            !!!next-token;
4551            redo B;            redo B;
4552          } elsif ($insertion_mode eq 'after body') {          } elsif ($self->{insertion_mode} eq 'after body') {
4553            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4554              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4555                ## As if in body                ## As if in body
4556                $reconstruct_active_formatting_elements->($insert_to_current);                $reconstruct_active_formatting_elements->($insert_to_current);
4557                                
4558                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4559    
4560                unless (length $token->{data}) {                unless (length $token->{data}) {
4561                  !!!next-token;                  !!!next-token;
# Line 4498  sub _construct_tree ($) { Line 4564  sub _construct_tree ($) {
4564              }              }
4565                            
4566              #              #
4567                !!!parse-error (type => 'after body:#'.$token->{type});
4568            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4569              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4570              $open_elements->[0]->[0]->append_child ($comment);              $self->{open_elements}->[0]->[0]->append_child ($comment);
4571              !!!next-token;              !!!next-token;
4572              redo B;              redo B;
4573              } elsif ($token->{type} eq 'start tag') {
4574                !!!parse-error (type => 'after body:'.$token->{tag_name});
4575                #
4576            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4577              if ($token->{tag_name} eq 'html') {              if ($token->{tag_name} eq 'html') {
4578                ## TODO: if inner_html, parse-error, ignore the token; otherwise,                if (defined $self->{inner_html_node}) {
4579                    !!!parse-error (type => 'unmatched end tag:html');
4580                $phase = 'trailing end';                  ## Ignore the token
4581                !!!next-token;                  !!!next-token;
4582                redo B;                  redo B;
4583                  } else {
4584                    $phase = 'trailing end';
4585                    !!!next-token;
4586                    redo B;
4587                  }
4588              } else {              } else {
4589                #                !!!parse-error (type => 'after body:/'.$token->{tag_name});
4590              }              }
4591            } else {            } else {
4592              #              !!!parse-error (type => 'after body:#'.$token->{type});
4593            }            }
4594    
4595            !!!parse-error ('data after body');            $self->{insertion_mode} = 'in body';
           $insertion_mode = 'in body';  
4596            ## reprocess            ## reprocess
4597            redo B;            redo B;
4598          } elsif ($insertion_mode eq 'in frameset') {          } elsif ($self->{insertion_mode} eq 'in frameset') {
4599            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4600              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4601                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4602    
4603                unless (length $token->{data}) {                unless (length $token->{data}) {
4604                  !!!next-token;                  !!!next-token;
# Line 4535  sub _construct_tree ($) { Line 4609  sub _construct_tree ($) {
4609              #              #
4610            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4611              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4612              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4613              !!!next-token;              !!!next-token;
4614              redo B;              redo B;
4615            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4545  sub _construct_tree ($) { Line 4619  sub _construct_tree ($) {
4619                redo B;                redo B;
4620              } elsif ($token->{tag_name} eq 'frame') {              } elsif ($token->{tag_name} eq 'frame') {
4621                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes});
4622                pop @$open_elements;                pop @{$self->{open_elements}};
4623                !!!next-token;                !!!next-token;
4624                redo B;                redo B;
4625              } elsif ($token->{tag_name} eq 'noframes') {              } elsif ($token->{tag_name} eq 'noframes') {
# Line 4556  sub _construct_tree ($) { Line 4630  sub _construct_tree ($) {
4630              }              }
4631            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} eq 'end tag') {
4632              if ($token->{tag_name} eq 'frameset') {              if ($token->{tag_name} eq 'frameset') {
4633                if ($open_elements->[-1]->[1] eq 'html' and                if ($self->{open_elements}->[-1]->[1] eq 'html' and
4634                    @$open_elements == 1) {                    @{$self->{open_elements}} == 1) {
4635                  !!!parse-error;                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4636                  ## Ignore the token                  ## Ignore the token
4637                  !!!next-token;                  !!!next-token;
4638                } else {                } else {
4639                  pop @$open_elements;                  pop @{$self->{open_elements}};
4640                  !!!next-token;                  !!!next-token;
4641                }                }
4642                                
4643                ## if not inner_html and                ## if not inner_html and
4644                if ($open_elements->[-1]->[1] ne 'frameset') {                if ($self->{open_elements}->[-1]->[1] ne 'frameset') {
4645                  $insertion_mode = 'after frameset';                  $self->{insertion_mode} = 'after frameset';
4646                }                }
4647                redo B;                redo B;
4648              } else {              } else {
# Line 4578  sub _construct_tree ($) { Line 4652  sub _construct_tree ($) {
4652              #              #
4653            }            }
4654                        
4655            !!!parse-error;            if (defined $token->{tag_name}) {
4656                !!!parse-error (type => 'in frameset:'.$token->{tag_name});
4657              } else {
4658                !!!parse-error (type => 'in frameset:#'.$token->{type});
4659              }
4660            ## Ignore the token            ## Ignore the token
4661            !!!next-token;            !!!next-token;
4662            redo B;            redo B;
4663          } elsif ($insertion_mode eq 'after frameset') {          } elsif ($self->{insertion_mode} eq 'after frameset') {
4664            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
4665              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4666                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4667    
4668                unless (length $token->{data}) {                unless (length $token->{data}) {
4669                  !!!next-token;                  !!!next-token;
# Line 4596  sub _construct_tree ($) { Line 4674  sub _construct_tree ($) {
4674              #              #
4675            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} eq 'comment') {
4676              my $comment = $self->{document}->create_comment ($token->{data});              my $comment = $self->{document}->create_comment ($token->{data});
4677              $open_elements->[-1]->[0]->append_child ($comment);              $self->{open_elements}->[-1]->[0]->append_child ($comment);
4678              !!!next-token;              !!!next-token;
4679              redo B;              redo B;
4680            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} eq 'start tag') {
# Line 4618  sub _construct_tree ($) { Line 4696  sub _construct_tree ($) {
4696              #              #
4697            }            }
4698                        
4699            !!!parse-error;            if (defined $token->{tag_name}) {
4700                !!!parse-error (type => 'after frameset:'.$token->{tag_name});
4701              } else {
4702                !!!parse-error (type => 'after frameset:#'.$token->{type});
4703              }
4704            ## Ignore the token            ## Ignore the token
4705            !!!next-token;            !!!next-token;
4706            redo B;            redo B;
4707    
4708            ## ISSUE: An issue in spec there            ## ISSUE: An issue in spec there
4709          } else {          } else {
4710            die "$0: $insertion_mode: Unknown insertion mode";            die "$0: $self->{insertion_mode}: Unknown insertion mode";
4711          }          }
4712        }        }
4713      } elsif ($phase eq 'trailing end') {      } elsif ($phase eq 'trailing end') {
4714        ## states in the main stage is preserved yet # MUST        ## states in the main stage is preserved yet # MUST
4715                
4716        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} eq 'DOCTYPE') {
4717          !!!parse-error;          !!!parse-error (type => 'after html:#DOCTYPE');
4718          ## Ignore the token          ## Ignore the token
4719          !!!next-token;          !!!next-token;
4720          redo B;          redo B;
# Line 4651  sub _construct_tree ($) { Line 4733  sub _construct_tree ($) {
4733            $reconstruct_active_formatting_elements->($insert_to_current)            $reconstruct_active_formatting_elements->($insert_to_current)
4734              if $phase eq 'main';              if $phase eq 'main';
4735                        
4736            $open_elements->[-1]->[0]->manakai_append_text ($data);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);
4737                        
4738            unless (length $token->{data}) {            unless (length $token->{data}) {
4739              !!!next-token;              !!!next-token;
# Line 4659  sub _construct_tree ($) { Line 4741  sub _construct_tree ($) {
4741            }            }
4742          }          }
4743    
4744          !!!parse-error;          !!!parse-error (type => 'after html:#character');
4745          $phase = 'main';          $phase = 'main';
4746          ## reprocess          ## reprocess
4747          redo B;          redo B;
4748        } elsif ($token->{type} eq 'start tag' or        } elsif ($token->{type} eq 'start tag' or
4749                 $token->{type} eq 'end tag') {                 $token->{type} eq 'end tag') {
4750          !!!parse-error;          !!!parse-error (type => 'after html:'.$token->{tag_name});
4751          $phase = 'main';          $phase = 'main';
4752          ## reprocess          ## reprocess
4753          redo B;          redo B;
# Line 4681  sub _construct_tree ($) { Line 4763  sub _construct_tree ($) {
4763    ## Stop parsing # MUST    ## Stop parsing # MUST
4764        
4765    ## TODO: script stuffs    ## TODO: script stuffs
4766  } # _construct_tree  } # _tree_construct_main
4767    
4768    sub set_inner_html ($$$) {
4769      my $class = shift;
4770      my $node = shift;
4771      my $s = \$_[0];
4772      my $onerror = $_[1];
4773    
4774      my $nt = $node->node_type;
4775      if ($nt == 9) {
4776        # MUST
4777        
4778        ## Step 1 # MUST
4779        ## TODO: If the document has an active parser, ...
4780        ## ISSUE: There is an issue in the spec.
4781        
4782        ## Step 2 # MUST
4783        my @cn = @{$node->child_nodes};
4784        for (@cn) {
4785          $node->remove_child ($_);
4786        }
4787    
4788        ## Step 3, 4, 5 # MUST
4789        $class->parse_string ($$s => $node, $onerror);
4790      } elsif ($nt == 1) {
4791        ## TODO: If non-html element
4792    
4793        ## NOTE: Most of this code is copied from |parse_string|
4794    
4795        ## Step 1 # MUST
4796        my $doc = $node->owner_document->implementation->create_document;
4797        ## TODO: Mark as HTML document
4798        my $p = $class->new;
4799        $p->{document} = $doc;
4800    
4801        ## Step 9 # MUST
4802        my $i = 0;
4803        my $line = 1;
4804        my $column = 0;
4805        $p->{set_next_input_character} = sub {
4806          my $self = shift;
4807          $self->{next_input_character} = -1 and return if $i >= length $$s;
4808          $self->{next_input_character} = ord substr $$s, $i++, 1;
4809          $column++;
4810          
4811          if ($self->{next_input_character} == 0x000D) { # CR
4812            if ($i >= length $$s) {
4813              #
4814            } else {
4815              my $next_char = ord substr $$s, $i++, 1;
4816              if ($next_char == 0x000A) { # LF
4817                #
4818              } else {
4819                push @{$self->{char}}, $next_char;
4820              }
4821            }
4822            $self->{next_input_character} = 0x000A; # LF # MUST
4823            $line++;
4824            $column = -1;
4825          } elsif ($self->{next_input_character} > 0x10FFFF) {
4826            $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
4827          } elsif ($self->{next_input_character} == 0x0000) { # NULL
4828            $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
4829          }
4830        };
4831        
4832        my $ponerror = $onerror || sub {
4833          my (%opt) = @_;
4834          warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";
4835        };
4836        $p->{parse_error} = sub {
4837          $ponerror->(@_, line => $line, column => $column);
4838        };
4839        
4840        $p->_initialize_tokenizer;
4841        $p->_initialize_tree_constructor;
4842    
4843        ## Step 2
4844        my $node_ln = $node->local_name;
4845        $p->{content_model_flag} = {
4846          title => 'RCDATA',
4847          textarea => 'RCDATA',
4848          style => 'CDATA',
4849          script => 'CDATA',
4850          xmp => 'CDATA',
4851          iframe => 'CDATA',
4852          noembed => 'CDATA',
4853          noframes => 'CDATA',
4854          noscript => 'CDATA',
4855          plaintext => 'PLAINTEXT',
4856        }->{$node_ln} || 'PCDATA';
4857           ## ISSUE: What is "the name of the element"? local name?
4858    
4859        $p->{inner_html_node} = [$node, $node_ln];
4860    
4861        ## Step 4
4862        my $root = $doc->create_element_ns
4863          ('http://www.w3.org/1999/xhtml', [undef, 'html']);
4864    
4865        ## Step 5 # MUST
4866        $doc->append_child ($root);
4867    
4868        ## Step 6 # MUST
4869        push @{$p->{open_elements}}, [$root, 'html'];
4870    
4871        undef $p->{head_element};
4872    
4873        ## Step 7 # MUST
4874        $p->_reset_insertion_mode;
4875    
4876        ## Step 8 # MUST
4877        my $anode = $node;
4878        AN: while (defined $anode) {
4879          if ($anode->node_type == 1) {
4880            my $nsuri = $anode->namespace_uri;
4881            if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
4882              if ($anode->local_name eq 'form') { ## TODO: case?
4883                $p->{form_element} = $anode;
4884                last AN;
4885              }
4886            }
4887          }
4888          $anode = $anode->parent_node;
4889        } # AN
4890        
4891        ## Step 3 # MUST
4892        ## Step 10 # MUST
4893        {
4894          my $self = $p;
4895          !!!next-token;
4896        }
4897        $p->_tree_construction_main;
4898    
4899        ## Step 11 # MUST
4900        my @cn = @{$node->child_nodes};
4901        for (@cn) {
4902          $node->remove_child ($_);
4903        }
4904        ## ISSUE: mutation events? read-only?
4905    
4906        ## Step 12 # MUST
4907        @cn = @{$root->child_nodes};
4908        for (@cn) {
4909          $node->append_child ($_);
4910        }
4911        ## ISSUE: adopt_node? mutation events?
4912    
4913        $p->_terminate_tree_constructor;
4914      } else {
4915        die "$0: |set_inner_html| is not defined for node of type $nt";
4916      }
4917    } # set_inner_html
4918    
4919    } # tree construction stage
4920    
4921  sub get_inner_html ($$$) {  sub get_inner_html ($$$) {
4922    my ($class, $node, $on_error) = @_;    my (undef, $node, $on_error) = @_;
4923    
4924    ## Step 1    ## Step 1
4925    my $s = '';    my $s = '';

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24