/[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.51 by wakaba, Sat Jul 21 11:46:41 2007 UTC revision 1.53 by wakaba, Sat Jul 21 12:37:57 2007 UTC
# Line 2494  sub _tree_construction_main ($) { Line 2494  sub _tree_construction_main ($) {
2494                         }                         }
2495    }; # $insert_to_foster    }; # $insert_to_foster
2496    
2497    my $in_body = sub {    my $insert;
     my $insert = shift;  
     if ($token->{type} eq 'start tag') {  
       if ($token->{tag_name} eq 'script') {  
         ## NOTE: This is an "as if in head" code clone  
         $script_start_tag->($insert);  
         return;  
       } elsif ($token->{tag_name} eq 'style') {  
         ## NOTE: This is an "as if in head" code clone  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ({  
                 base => 1, link => 1,  
                }->{$token->{tag_name}}) {  
         ## NOTE: This is an "as if in head" code clone, only "-t" differs  
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'meta') {  
         ## NOTE: This is an "as if in head" code clone, only "-t" differs  
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
   
         unless ($self->{confident}) {  
           my $charset;  
           if ($token->{attributes}->{charset}) { ## TODO: And if supported  
             $charset = $token->{attributes}->{charset}->{value};  
           }  
           if ($token->{attributes}->{'http-equiv'}) {  
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
             if ($token->{attributes}->{'http-equiv'}->{value}  
                 =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=  
                     [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|  
                     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {  
               $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
             } ## TODO: And if supported  
           }  
           ## TODO: Change the encoding  
         }  
   
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'title') {  
         !!!parse-error (type => 'in body:title');  
         ## NOTE: This is an "as if in head" code clone  
         $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {  
           if (defined $self->{head_element}) {  
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, p => 1, ul => 1,  
                 pre => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         if ($token->{tag_name} eq 'pre') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
         } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'li') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model} = PLAINTEXT_CONTENT_MODEL;  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error (type => 'in a:a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
   
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
   
         !!!next-token;  
         return;  
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'nobr') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
   
         ## has a |nobr| element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'nobr') {  
             !!!parse-error (type => 'not closed:nobr');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
   
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
         }  
   
         ## NOTE: There is an "as if <br>" code clone.  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           my $form_attrs;  
           $form_attrs->{action} = $at->{action} if $at->{action};  
           my $prompt_attr = $at->{prompt};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           delete $at->{action};  
           delete $at->{prompt};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form',  
                          attributes => $form_attrs},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                        );  
           if ($prompt_attr) {  
             push @tokens, {type => 'character', data => $prompt_attr->{value}};  
           } else {  
             push @tokens, {type => 'character',  
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
           }  
           push @tokens,  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'};  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model} = RCDATA_CONTENT_MODEL;  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!next-token;  
           }  
         }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq $tag_name) {  
           ## Ignore the token  
         } else {  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 iframe => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         ## NOTE: There are two "as if in body" code clones.  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
       } else {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
       }  
     } elsif ($token->{type} eq 'end tag') {  
       if ($token->{tag_name} eq 'body') {  
         if (@{$self->{open_elements}} > 1 and  
             $self->{open_elements}->[1]->[1] eq 'body') {  
           for (@{$self->{open_elements}}) {  
             unless ({  
                        dd => 1, dt => 1, li => 1, p => 1, td => 1,  
                        th => 1, tr => 1, body => 1, html => 1,  
                      tbody => 1, tfoot => 1, thead => 1,  
                     }->{$_->[1]}) {  
               !!!parse-error (type => 'not closed:'.$_->[1]);  
             }  
           }  
   
           $self->{insertion_mode} = 'after body';  
           !!!next-token;  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
           }  
           $self->{insertion_mode} = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           if (defined $i) {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
           } else {  
             !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           }  
         }  
           
         if (defined $i) {  
           splice @{$self->{open_elements}}, $i;  
         } elsif ($token->{tag_name} eq 'p') {  
           ## As if <p>, then reprocess the current token  
           my $el;  
           !!!create-element ($el, 'p');  
           $insert->($el);  
         }  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         ## has an element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
           pop @{$self->{open_elements}};  
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         undef $self->{form_element};  
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
   
         ## As if <br>  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         my $el;  
         !!!create-element ($el, 'br');  
         $insert->($el);  
           
         ## Ignore the token.  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
   
         ## Step 2  
         S2: {  
           if ($node->[1] eq $token->{tag_name}) {  
             ## Step 1  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
           
             ## Step 2  
             if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
             }  
               
             ## Step 3  
             splice @{$self->{open_elements}}, $node_i;  
   
             !!!next-token;  
             last S2;  
           } else {  
             ## Step 3  
             if (not $formatting_category->{$node->[1]} and  
                 #not $phrasing_category->{$node->[1]} and  
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
           }  
             
           ## Step 4  
           $node_i--;  
           $node = $self->{open_elements}->[$node_i];  
             
           ## Step 5;  
           redo S2;  
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
2498    
2499    B: {    B: {
2500      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} eq 'DOCTYPE') {
# Line 4106  sub _tree_construction_main ($) { Line 3262  sub _tree_construction_main ($) {
3262              } else {              } else {
3263                #                #
3264              }              }
3265            } else {        } else {
3266              #          die "$0: $token->{type}: Unknown token type";
3267            }        }
3268              
3269            $in_body->($insert_to_current);        $insert = $insert_to_current;
3270            redo B;        #
3271      } elsif ($self->{insertion_mode} eq 'in row' or      } elsif ($self->{insertion_mode} eq 'in row' or
3272               $self->{insertion_mode} eq 'in table body' or               $self->{insertion_mode} eq 'in table body' or
3273               $self->{insertion_mode} eq 'in table') {               $self->{insertion_mode} eq 'in table') {
# Line 4675  sub _tree_construction_main ($) { Line 3831  sub _tree_construction_main ($) {
3831              die "$0: $token->{type}: Unknown token type";              die "$0: $token->{type}: Unknown token type";
3832            }            }
3833    
3834            !!!parse-error (type => 'in table:'.$token->{tag_name});        !!!parse-error (type => 'in table:'.$token->{tag_name});
3835            $in_body->($insert_to_foster);  
3836            redo B;        $insert = $insert_to_foster;
3837          #
3838      } elsif ($self->{insertion_mode} eq 'in column group') {      } elsif ($self->{insertion_mode} eq 'in column group') {
3839            if ($token->{type} eq 'character') {            if ($token->{type} eq 'character') {
3840              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
# Line 5097  sub _tree_construction_main ($) { Line 4254  sub _tree_construction_main ($) {
4254      } else {      } else {
4255        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4256      }      }
4257    
4258        ## "in body" insertion mode
4259        if ($token->{type} eq 'start tag') {
4260          if ($token->{tag_name} eq 'script') {
4261            ## NOTE: This is an "as if in head" code clone
4262            $script_start_tag->($insert);
4263            redo B;
4264          } elsif ($token->{tag_name} eq 'style') {
4265            ## NOTE: This is an "as if in head" code clone
4266            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4267            redo B;
4268          } elsif ({
4269                    base => 1, link => 1,
4270                   }->{$token->{tag_name}}) {
4271            ## NOTE: This is an "as if in head" code clone, only "-t" differs
4272            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4273            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4274            !!!next-token;
4275            redo B;
4276          } elsif ($token->{tag_name} eq 'meta') {
4277            ## NOTE: This is an "as if in head" code clone, only "-t" differs
4278            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4279            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4280    
4281            unless ($self->{confident}) {
4282              my $charset;
4283              if ($token->{attributes}->{charset}) { ## TODO: And if supported
4284                $charset = $token->{attributes}->{charset}->{value};
4285              }
4286              if ($token->{attributes}->{'http-equiv'}) {
4287                ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.
4288                if ($token->{attributes}->{'http-equiv'}->{value}
4289                    =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=
4290                        [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4291                        ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {
4292                  $charset = defined $1 ? $1 : defined $2 ? $2 : $3;
4293                } ## TODO: And if supported
4294              }
4295              ## TODO: Change the encoding
4296            }
4297    
4298            !!!next-token;
4299            redo B;
4300          } elsif ($token->{tag_name} eq 'title') {
4301            !!!parse-error (type => 'in body:title');
4302            ## NOTE: This is an "as if in head" code clone
4303            $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {
4304              if (defined $self->{head_element}) {
4305                $self->{head_element}->append_child ($_[0]);
4306              } else {
4307                $insert->($_[0]);
4308              }
4309            });
4310            redo B;
4311          } elsif ($token->{tag_name} eq 'body') {
4312            !!!parse-error (type => 'in body:body');
4313                  
4314            if (@{$self->{open_elements}} == 1 or
4315                $self->{open_elements}->[1]->[1] ne 'body') {
4316              ## Ignore the token
4317            } else {
4318              my $body_el = $self->{open_elements}->[1]->[0];
4319              for my $attr_name (keys %{$token->{attributes}}) {
4320                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
4321                  $body_el->set_attribute_ns
4322                    (undef, [undef, $attr_name],
4323                     $token->{attributes}->{$attr_name}->{value});
4324                }
4325              }
4326            }
4327            !!!next-token;
4328            redo B;
4329          } elsif ({
4330                    address => 1, blockquote => 1, center => 1, dir => 1,
4331                    div => 1, dl => 1, fieldset => 1, listing => 1,
4332                    menu => 1, ol => 1, p => 1, ul => 1,
4333                    pre => 1,
4334                   }->{$token->{tag_name}}) {
4335            ## has a p element in scope
4336            INSCOPE: for (reverse @{$self->{open_elements}}) {
4337              if ($_->[1] eq 'p') {
4338                !!!back-token;
4339                $token = {type => 'end tag', tag_name => 'p'};
4340                redo B;
4341              } elsif ({
4342                        table => 1, caption => 1, td => 1, th => 1,
4343                        button => 1, marquee => 1, object => 1, html => 1,
4344                       }->{$_->[1]}) {
4345                last INSCOPE;
4346              }
4347            } # INSCOPE
4348              
4349            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4350            if ($token->{tag_name} eq 'pre') {
4351              !!!next-token;
4352              if ($token->{type} eq 'character') {
4353                $token->{data} =~ s/^\x0A//;
4354                unless (length $token->{data}) {
4355                  !!!next-token;
4356                }
4357              }
4358            } else {
4359              !!!next-token;
4360            }
4361            redo B;
4362          } elsif ($token->{tag_name} eq 'form') {
4363            if (defined $self->{form_element}) {
4364              !!!parse-error (type => 'in form:form');
4365              ## Ignore the token
4366              !!!next-token;
4367              redo B;
4368            } else {
4369              ## has a p element in scope
4370              INSCOPE: for (reverse @{$self->{open_elements}}) {
4371                if ($_->[1] eq 'p') {
4372                  !!!back-token;
4373                  $token = {type => 'end tag', tag_name => 'p'};
4374                  redo B;
4375                } elsif ({
4376                          table => 1, caption => 1, td => 1, th => 1,
4377                          button => 1, marquee => 1, object => 1, html => 1,
4378                         }->{$_->[1]}) {
4379                  last INSCOPE;
4380                }
4381              } # INSCOPE
4382                
4383              !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4384              $self->{form_element} = $self->{open_elements}->[-1]->[0];
4385              !!!next-token;
4386              redo B;
4387            }
4388          } elsif ($token->{tag_name} eq 'li') {
4389            ## has a p element in scope
4390            INSCOPE: for (reverse @{$self->{open_elements}}) {
4391              if ($_->[1] eq 'p') {
4392                !!!back-token;
4393                $token = {type => 'end tag', tag_name => 'p'};
4394                redo B;
4395              } elsif ({
4396                        table => 1, caption => 1, td => 1, th => 1,
4397                        button => 1, marquee => 1, object => 1, html => 1,
4398                       }->{$_->[1]}) {
4399                last INSCOPE;
4400              }
4401            } # INSCOPE
4402              
4403            ## Step 1
4404            my $i = -1;
4405            my $node = $self->{open_elements}->[$i];
4406            LI: {
4407              ## Step 2
4408              if ($node->[1] eq 'li') {
4409                if ($i != -1) {
4410                  !!!parse-error (type => 'end tag missing:'.
4411                                  $self->{open_elements}->[-1]->[1]);
4412                }
4413                splice @{$self->{open_elements}}, $i;
4414                last LI;
4415              }
4416              
4417              ## Step 3
4418              if (not $formatting_category->{$node->[1]} and
4419                  #not $phrasing_category->{$node->[1]} and
4420                  ($special_category->{$node->[1]} or
4421                   $scoping_category->{$node->[1]}) and
4422                  $node->[1] ne 'address' and $node->[1] ne 'div') {
4423                last LI;
4424              }
4425              
4426              ## Step 4
4427              $i--;
4428              $node = $self->{open_elements}->[$i];
4429              redo LI;
4430            } # LI
4431              
4432            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4433            !!!next-token;
4434            redo B;
4435          } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {
4436            ## has a p element in scope
4437            INSCOPE: for (reverse @{$self->{open_elements}}) {
4438              if ($_->[1] eq 'p') {
4439                !!!back-token;
4440                $token = {type => 'end tag', tag_name => 'p'};
4441                redo B;
4442              } elsif ({
4443                        table => 1, caption => 1, td => 1, th => 1,
4444                        button => 1, marquee => 1, object => 1, html => 1,
4445                       }->{$_->[1]}) {
4446                last INSCOPE;
4447              }
4448            } # INSCOPE
4449              
4450            ## Step 1
4451            my $i = -1;
4452            my $node = $self->{open_elements}->[$i];
4453            LI: {
4454              ## Step 2
4455              if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {
4456                if ($i != -1) {
4457                  !!!parse-error (type => 'end tag missing:'.
4458                                  $self->{open_elements}->[-1]->[1]);
4459                }
4460                splice @{$self->{open_elements}}, $i;
4461                last LI;
4462              }
4463              
4464              ## Step 3
4465              if (not $formatting_category->{$node->[1]} and
4466                  #not $phrasing_category->{$node->[1]} and
4467                  ($special_category->{$node->[1]} or
4468                   $scoping_category->{$node->[1]}) and
4469                  $node->[1] ne 'address' and $node->[1] ne 'div') {
4470                last LI;
4471              }
4472              
4473              ## Step 4
4474              $i--;
4475              $node = $self->{open_elements}->[$i];
4476              redo LI;
4477            } # LI
4478              
4479            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4480            !!!next-token;
4481            redo B;
4482          } elsif ($token->{tag_name} eq 'plaintext') {
4483            ## has a p element in scope
4484            INSCOPE: for (reverse @{$self->{open_elements}}) {
4485              if ($_->[1] eq 'p') {
4486                !!!back-token;
4487                $token = {type => 'end tag', tag_name => 'p'};
4488                redo B;
4489              } elsif ({
4490                        table => 1, caption => 1, td => 1, th => 1,
4491                        button => 1, marquee => 1, object => 1, html => 1,
4492                       }->{$_->[1]}) {
4493                last INSCOPE;
4494              }
4495            } # INSCOPE
4496              
4497            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4498              
4499            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
4500              
4501            !!!next-token;
4502            redo B;
4503          } elsif ({
4504                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
4505                   }->{$token->{tag_name}}) {
4506            ## has a p element in scope
4507            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4508              my $node = $self->{open_elements}->[$_];
4509              if ($node->[1] eq 'p') {
4510                !!!back-token;
4511                $token = {type => 'end tag', tag_name => 'p'};
4512                redo B;
4513              } elsif ({
4514                        table => 1, caption => 1, td => 1, th => 1,
4515                        button => 1, marquee => 1, object => 1, html => 1,
4516                       }->{$node->[1]}) {
4517                last INSCOPE;
4518              }
4519            } # INSCOPE
4520              
4521            ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>
4522            ## has an element in scope
4523            #my $i;
4524            #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4525            #  my $node = $self->{open_elements}->[$_];
4526            #  if ({
4527            #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
4528            #      }->{$node->[1]}) {
4529            #    $i = $_;
4530            #    last INSCOPE;
4531            #  } elsif ({
4532            #            table => 1, caption => 1, td => 1, th => 1,
4533            #            button => 1, marquee => 1, object => 1, html => 1,
4534            #           }->{$node->[1]}) {
4535            #    last INSCOPE;
4536            #  }
4537            #} # INSCOPE
4538            #  
4539            #if (defined $i) {
4540            #  !!! parse-error (type => 'in hn:hn');
4541            #  splice @{$self->{open_elements}}, $i;
4542            #}
4543              
4544            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4545              
4546            !!!next-token;
4547            redo B;
4548          } elsif ($token->{tag_name} eq 'a') {
4549            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4550              my $node = $active_formatting_elements->[$i];
4551              if ($node->[1] eq 'a') {
4552                !!!parse-error (type => 'in a:a');
4553                
4554                !!!back-token;
4555                $token = {type => 'end tag', tag_name => 'a'};
4556                $formatting_end_tag->($token->{tag_name});
4557                
4558                AFE2: for (reverse 0..$#$active_formatting_elements) {
4559                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4560                    splice @$active_formatting_elements, $_, 1;
4561                    last AFE2;
4562                  }
4563                } # AFE2
4564                OE: for (reverse 0..$#{$self->{open_elements}}) {
4565                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
4566                    splice @{$self->{open_elements}}, $_, 1;
4567                    last OE;
4568                  }
4569                } # OE
4570                last AFE;
4571              } elsif ($node->[0] eq '#marker') {
4572                last AFE;
4573              }
4574            } # AFE
4575              
4576            $reconstruct_active_formatting_elements->($insert_to_current);
4577    
4578            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4579            push @$active_formatting_elements, $self->{open_elements}->[-1];
4580    
4581            !!!next-token;
4582            redo B;
4583          } elsif ({
4584                    b => 1, big => 1, em => 1, font => 1, i => 1,
4585                    s => 1, small => 1, strile => 1,
4586                    strong => 1, tt => 1, u => 1,
4587                   }->{$token->{tag_name}}) {
4588            $reconstruct_active_formatting_elements->($insert_to_current);
4589            
4590            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4591            push @$active_formatting_elements, $self->{open_elements}->[-1];
4592            
4593            !!!next-token;
4594            redo B;
4595          } elsif ($token->{tag_name} eq 'nobr') {
4596            $reconstruct_active_formatting_elements->($insert_to_current);
4597    
4598            ## has a |nobr| element in scope
4599            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4600              my $node = $self->{open_elements}->[$_];
4601              if ($node->[1] eq 'nobr') {
4602                !!!parse-error (type => 'not closed:nobr');
4603                !!!back-token;
4604                $token = {type => 'end tag', tag_name => 'nobr'};
4605                redo B;
4606              } elsif ({
4607                        table => 1, caption => 1, td => 1, th => 1,
4608                        button => 1, marquee => 1, object => 1, html => 1,
4609                       }->{$node->[1]}) {
4610                last INSCOPE;
4611              }
4612            } # INSCOPE
4613            
4614            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4615            push @$active_formatting_elements, $self->{open_elements}->[-1];
4616            
4617            !!!next-token;
4618            redo B;
4619          } elsif ($token->{tag_name} eq 'button') {
4620            ## has a button element in scope
4621            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4622              my $node = $self->{open_elements}->[$_];
4623              if ($node->[1] eq 'button') {
4624                !!!parse-error (type => 'in button:button');
4625                !!!back-token;
4626                $token = {type => 'end tag', tag_name => 'button'};
4627                redo B;
4628              } elsif ({
4629                        table => 1, caption => 1, td => 1, th => 1,
4630                        button => 1, marquee => 1, object => 1, html => 1,
4631                       }->{$node->[1]}) {
4632                last INSCOPE;
4633              }
4634            } # INSCOPE
4635              
4636            $reconstruct_active_formatting_elements->($insert_to_current);
4637              
4638            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4639            push @$active_formatting_elements, ['#marker', ''];
4640    
4641            !!!next-token;
4642            redo B;
4643          } elsif ($token->{tag_name} eq 'marquee' or
4644                   $token->{tag_name} eq 'object') {
4645            $reconstruct_active_formatting_elements->($insert_to_current);
4646            
4647            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4648            push @$active_formatting_elements, ['#marker', ''];
4649            
4650            !!!next-token;
4651            redo B;
4652          } elsif ($token->{tag_name} eq 'xmp') {
4653            $reconstruct_active_formatting_elements->($insert_to_current);
4654            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4655            redo B;
4656          } elsif ($token->{tag_name} eq 'table') {
4657            ## has a p element in scope
4658            INSCOPE: for (reverse @{$self->{open_elements}}) {
4659              if ($_->[1] eq 'p') {
4660                !!!back-token;
4661                $token = {type => 'end tag', tag_name => 'p'};
4662                redo B;
4663              } elsif ({
4664                        table => 1, caption => 1, td => 1, th => 1,
4665                        button => 1, marquee => 1, object => 1, html => 1,
4666                       }->{$_->[1]}) {
4667                last INSCOPE;
4668              }
4669            } # INSCOPE
4670              
4671            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4672              
4673            $self->{insertion_mode} = 'in table';
4674              
4675            !!!next-token;
4676            redo B;
4677          } elsif ({
4678                    area => 1, basefont => 1, bgsound => 1, br => 1,
4679                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
4680                    image => 1,
4681                   }->{$token->{tag_name}}) {
4682            if ($token->{tag_name} eq 'image') {
4683              !!!parse-error (type => 'image');
4684              $token->{tag_name} = 'img';
4685            }
4686    
4687            ## NOTE: There is an "as if <br>" code clone.
4688            $reconstruct_active_formatting_elements->($insert_to_current);
4689            
4690            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4691            pop @{$self->{open_elements}};
4692            
4693            !!!next-token;
4694            redo B;
4695          } elsif ($token->{tag_name} eq 'hr') {
4696            ## has a p element in scope
4697            INSCOPE: for (reverse @{$self->{open_elements}}) {
4698              if ($_->[1] eq 'p') {
4699                !!!back-token;
4700                $token = {type => 'end tag', tag_name => 'p'};
4701                redo B;
4702              } elsif ({
4703                        table => 1, caption => 1, td => 1, th => 1,
4704                        button => 1, marquee => 1, object => 1, html => 1,
4705                       }->{$_->[1]}) {
4706                last INSCOPE;
4707              }
4708            } # INSCOPE
4709              
4710            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4711            pop @{$self->{open_elements}};
4712              
4713            !!!next-token;
4714            redo B;
4715          } elsif ($token->{tag_name} eq 'input') {
4716            $reconstruct_active_formatting_elements->($insert_to_current);
4717            
4718            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4719            ## TODO: associate with $self->{form_element} if defined
4720            pop @{$self->{open_elements}};
4721            
4722            !!!next-token;
4723            redo B;
4724          } elsif ($token->{tag_name} eq 'isindex') {
4725            !!!parse-error (type => 'isindex');
4726            
4727            if (defined $self->{form_element}) {
4728              ## Ignore the token
4729              !!!next-token;
4730              redo B;
4731            } else {
4732              my $at = $token->{attributes};
4733              my $form_attrs;
4734              $form_attrs->{action} = $at->{action} if $at->{action};
4735              my $prompt_attr = $at->{prompt};
4736              $at->{name} = {name => 'name', value => 'isindex'};
4737              delete $at->{action};
4738              delete $at->{prompt};
4739              my @tokens = (
4740                            {type => 'start tag', tag_name => 'form',
4741                             attributes => $form_attrs},
4742                            {type => 'start tag', tag_name => 'hr'},
4743                            {type => 'start tag', tag_name => 'p'},
4744                            {type => 'start tag', tag_name => 'label'},
4745                           );
4746              if ($prompt_attr) {
4747                push @tokens, {type => 'character', data => $prompt_attr->{value}};
4748              } else {
4749                push @tokens, {type => 'character',
4750                               data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD
4751                ## TODO: make this configurable
4752              }
4753              push @tokens,
4754                            {type => 'start tag', tag_name => 'input', attributes => $at},
4755                            #{type => 'character', data => ''}, # SHOULD
4756                            {type => 'end tag', tag_name => 'label'},
4757                            {type => 'end tag', tag_name => 'p'},
4758                            {type => 'start tag', tag_name => 'hr'},
4759                            {type => 'end tag', tag_name => 'form'};
4760              $token = shift @tokens;
4761              !!!back-token (@tokens);
4762              redo B;
4763            }
4764          } elsif ($token->{tag_name} eq 'textarea') {
4765            my $tag_name = $token->{tag_name};
4766            my $el;
4767            !!!create-element ($el, $token->{tag_name}, $token->{attributes});
4768            
4769            ## TODO: $self->{form_element} if defined
4770            $self->{content_model} = RCDATA_CONTENT_MODEL;
4771            delete $self->{escape}; # MUST
4772            
4773            $insert->($el);
4774            
4775            my $text = '';
4776            !!!next-token;
4777            if ($token->{type} eq 'character') {
4778              $token->{data} =~ s/^\x0A//;
4779              unless (length $token->{data}) {
4780                !!!next-token;
4781              }
4782            }
4783            while ($token->{type} eq 'character') {
4784              $text .= $token->{data};
4785              !!!next-token;
4786            }
4787            if (length $text) {
4788              $el->manakai_append_text ($text);
4789            }
4790            
4791            $self->{content_model} = PCDATA_CONTENT_MODEL;
4792            
4793            if ($token->{type} eq 'end tag' and
4794                $token->{tag_name} eq $tag_name) {
4795              ## Ignore the token
4796            } else {
4797              !!!parse-error (type => 'in RCDATA:#'.$token->{type});
4798            }
4799            !!!next-token;
4800            redo B;
4801          } elsif ({
4802                    iframe => 1,
4803                    noembed => 1,
4804                    noframes => 1,
4805                    noscript => 0, ## TODO: 1 if scripting is enabled
4806                   }->{$token->{tag_name}}) {
4807            ## NOTE: There are two "as if in body" code clones.
4808            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);
4809            redo B;
4810          } elsif ($token->{tag_name} eq 'select') {
4811            $reconstruct_active_formatting_elements->($insert_to_current);
4812            
4813            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4814            
4815            $self->{insertion_mode} = 'in select';
4816            !!!next-token;
4817            redo B;
4818          } elsif ({
4819                    caption => 1, col => 1, colgroup => 1, frame => 1,
4820                    frameset => 1, head => 1, option => 1, optgroup => 1,
4821                    tbody => 1, td => 1, tfoot => 1, th => 1,
4822                    thead => 1, tr => 1,
4823                   }->{$token->{tag_name}}) {
4824            !!!parse-error (type => 'in body:'.$token->{tag_name});
4825            ## Ignore the token
4826            !!!next-token;
4827            redo B;
4828            
4829            ## ISSUE: An issue on HTML5 new elements in the spec.
4830          } else {
4831            $reconstruct_active_formatting_elements->($insert_to_current);
4832            
4833            !!!insert-element-t ($token->{tag_name}, $token->{attributes});
4834            
4835            !!!next-token;
4836            redo B;
4837          }
4838        } elsif ($token->{type} eq 'end tag') {
4839          if ($token->{tag_name} eq 'body') {
4840            if (@{$self->{open_elements}} > 1 and
4841                $self->{open_elements}->[1]->[1] eq 'body') {
4842              for (@{$self->{open_elements}}) {
4843                unless ({
4844                           dd => 1, dt => 1, li => 1, p => 1, td => 1,
4845                           th => 1, tr => 1, body => 1, html => 1,
4846                         tbody => 1, tfoot => 1, thead => 1,
4847                        }->{$_->[1]}) {
4848                  !!!parse-error (type => 'not closed:'.$_->[1]);
4849                }
4850              }
4851    
4852              $self->{insertion_mode} = 'after body';
4853              !!!next-token;
4854              redo B;
4855            } else {
4856              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4857              ## Ignore the token
4858              !!!next-token;
4859              redo B;
4860            }
4861          } elsif ($token->{tag_name} eq 'html') {
4862            if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {
4863              ## ISSUE: There is an issue in the spec.
4864              if ($self->{open_elements}->[-1]->[1] ne 'body') {
4865                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);
4866              }
4867              $self->{insertion_mode} = 'after body';
4868              ## reprocess
4869              redo B;
4870            } else {
4871              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4872              ## Ignore the token
4873              !!!next-token;
4874              redo B;
4875            }
4876          } elsif ({
4877                    address => 1, blockquote => 1, center => 1, dir => 1,
4878                    div => 1, dl => 1, fieldset => 1, listing => 1,
4879                    menu => 1, ol => 1, pre => 1, ul => 1,
4880                    p => 1,
4881                    dd => 1, dt => 1, li => 1,
4882                    button => 1, marquee => 1, object => 1,
4883                   }->{$token->{tag_name}}) {
4884            ## has an element in scope
4885            my $i;
4886            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4887              my $node = $self->{open_elements}->[$_];
4888              if ($node->[1] eq $token->{tag_name}) {
4889                ## generate implied end tags
4890                if ({
4891                     dd => ($token->{tag_name} ne 'dd'),
4892                     dt => ($token->{tag_name} ne 'dt'),
4893                     li => ($token->{tag_name} ne 'li'),
4894                     p => ($token->{tag_name} ne 'p'),
4895                     td => 1, th => 1, tr => 1,
4896                     tbody => 1, tfoot=> 1, thead => 1,
4897                    }->{$self->{open_elements}->[-1]->[1]}) {
4898                  !!!back-token;
4899                  $token = {type => 'end tag',
4900                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4901                  redo B;
4902                }
4903                $i = $_;
4904                last INSCOPE unless $token->{tag_name} eq 'p';
4905              } elsif ({
4906                        table => 1, caption => 1, td => 1, th => 1,
4907                        button => 1, marquee => 1, object => 1, html => 1,
4908                       }->{$node->[1]}) {
4909                last INSCOPE;
4910              }
4911            } # INSCOPE
4912            
4913            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
4914              if (defined $i) {
4915                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4916              } else {
4917                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
4918              }
4919            }
4920            
4921            if (defined $i) {
4922              splice @{$self->{open_elements}}, $i;
4923            } elsif ($token->{tag_name} eq 'p') {
4924              ## As if <p>, then reprocess the current token
4925              my $el;
4926              !!!create-element ($el, 'p');
4927              $insert->($el);
4928            }
4929            $clear_up_to_marker->()
4930              if {
4931                button => 1, marquee => 1, object => 1,
4932              }->{$token->{tag_name}};
4933            !!!next-token;
4934            redo B;
4935          } elsif ($token->{tag_name} eq 'form') {
4936            ## has an element in scope
4937            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4938              my $node = $self->{open_elements}->[$_];
4939              if ($node->[1] eq $token->{tag_name}) {
4940                ## generate implied end tags
4941                if ({
4942                     dd => 1, dt => 1, li => 1, p => 1,
4943                     td => 1, th => 1, tr => 1,
4944                     tbody => 1, tfoot=> 1, thead => 1,
4945                    }->{$self->{open_elements}->[-1]->[1]}) {
4946                  !!!back-token;
4947                  $token = {type => 'end tag',
4948                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4949                  redo B;
4950                }
4951                last INSCOPE;
4952              } elsif ({
4953                        table => 1, caption => 1, td => 1, th => 1,
4954                        button => 1, marquee => 1, object => 1, html => 1,
4955                       }->{$node->[1]}) {
4956                last INSCOPE;
4957              }
4958            } # INSCOPE
4959            
4960            if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {
4961              pop @{$self->{open_elements}};
4962            } else {
4963              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
4964            }
4965    
4966            undef $self->{form_element};
4967            !!!next-token;
4968            redo B;
4969          } elsif ({
4970                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
4971                   }->{$token->{tag_name}}) {
4972            ## has an element in scope
4973            my $i;
4974            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4975              my $node = $self->{open_elements}->[$_];
4976              if ({
4977                   h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
4978                  }->{$node->[1]}) {
4979                ## generate implied end tags
4980                if ({
4981                     dd => 1, dt => 1, li => 1, p => 1,
4982                     td => 1, th => 1, tr => 1,
4983                     tbody => 1, tfoot=> 1, thead => 1,
4984                    }->{$self->{open_elements}->[-1]->[1]}) {
4985                  !!!back-token;
4986                  $token = {type => 'end tag',
4987                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
4988                  redo B;
4989                }
4990                $i = $_;
4991                last INSCOPE;
4992              } elsif ({
4993                        table => 1, caption => 1, td => 1, th => 1,
4994                        button => 1, marquee => 1, object => 1, html => 1,
4995                       }->{$node->[1]}) {
4996                last INSCOPE;
4997              }
4998            } # INSCOPE
4999            
5000            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {
5001              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
5002            }
5003            
5004            splice @{$self->{open_elements}}, $i if defined $i;
5005            !!!next-token;
5006            redo B;
5007          } elsif ({
5008                    a => 1,
5009                    b => 1, big => 1, em => 1, font => 1, i => 1,
5010                    nobr => 1, s => 1, small => 1, strile => 1,
5011                    strong => 1, tt => 1, u => 1,
5012                   }->{$token->{tag_name}}) {
5013            $formatting_end_tag->($token->{tag_name});
5014            redo B;
5015          } elsif ($token->{tag_name} eq 'br') {
5016            !!!parse-error (type => 'unmatched end tag:br');
5017    
5018            ## As if <br>
5019            $reconstruct_active_formatting_elements->($insert_to_current);
5020            
5021            my $el;
5022            !!!create-element ($el, 'br');
5023            $insert->($el);
5024            
5025            ## Ignore the token.
5026            !!!next-token;
5027            redo B;
5028          } elsif ({
5029                    caption => 1, col => 1, colgroup => 1, frame => 1,
5030                    frameset => 1, head => 1, option => 1, optgroup => 1,
5031                    tbody => 1, td => 1, tfoot => 1, th => 1,
5032                    thead => 1, tr => 1,
5033                    area => 1, basefont => 1, bgsound => 1,
5034                    embed => 1, hr => 1, iframe => 1, image => 1,
5035                    img => 1, input => 1, isindex => 1, noembed => 1,
5036                    noframes => 1, param => 1, select => 1, spacer => 1,
5037                    table => 1, textarea => 1, wbr => 1,
5038                    noscript => 0, ## TODO: if scripting is enabled
5039                   }->{$token->{tag_name}}) {
5040            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5041            ## Ignore the token
5042            !!!next-token;
5043            redo B;
5044            
5045            ## ISSUE: Issue on HTML5 new elements in spec
5046            
5047          } else {
5048            ## Step 1
5049            my $node_i = -1;
5050            my $node = $self->{open_elements}->[$node_i];
5051    
5052            ## Step 2
5053            S2: {
5054              if ($node->[1] eq $token->{tag_name}) {
5055                ## Step 1
5056                ## generate implied end tags
5057                if ({
5058                     dd => 1, dt => 1, li => 1, p => 1,
5059                     td => 1, th => 1, tr => 1,
5060                     tbody => 1, tfoot=> 1, thead => 1,
5061                    }->{$self->{open_elements}->[-1]->[1]}) {
5062                  !!!back-token;
5063                  $token = {type => 'end tag',
5064                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST
5065                  redo B;
5066                }
5067            
5068                ## Step 2
5069                if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {
5070                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);
5071                }
5072                
5073                ## Step 3
5074                splice @{$self->{open_elements}}, $node_i;
5075    
5076                !!!next-token;
5077                last S2;
5078              } else {
5079                ## Step 3
5080                if (not $formatting_category->{$node->[1]} and
5081                    #not $phrasing_category->{$node->[1]} and
5082                    ($special_category->{$node->[1]} or
5083                     $scoping_category->{$node->[1]})) {
5084                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});
5085                  ## Ignore the token
5086                  !!!next-token;
5087                  last S2;
5088                }
5089              }
5090              
5091              ## Step 4
5092              $node_i--;
5093              $node = $self->{open_elements}->[$node_i];
5094              
5095              ## Step 5;
5096              redo S2;
5097            } # S2
5098            redo B;
5099          }
5100        }
5101        redo B;
5102    } # B    } # B
5103    
5104    ## NOTE: The "trailing end" phase in HTML5 is split into    ## NOTE: The "trailing end" phase in HTML5 is split into

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24