/[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.72 by wakaba, Sun Mar 2 14:32:26 2008 UTC revision 1.201 by wakaba, Sat Oct 4 12:20:35 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91      BODY_EL |
92      HTML_EL |
93      TABLE_CELL_EL |
94      TABLE_ROW_EL |
95      TABLE_ROW_GROUP_EL
96    }
97    
98    sub SCOPING_EL () {
99      BUTTON_EL |
100      CAPTION_EL |
101      HTML_EL |
102      TABLE_EL |
103      TABLE_CELL_EL |
104      MISC_SCOPING_EL
105    }
106    
107    sub TABLE_SCOPING_EL () {
108      HTML_EL |
109      TABLE_EL
110    }
111    
112    sub TABLE_ROWS_SCOPING_EL () {
113      HTML_EL |
114      TABLE_ROW_GROUP_EL
115    }
116    
117    sub TABLE_ROW_SCOPING_EL () {
118      HTML_EL |
119      TABLE_ROW_EL
120    }
121    
122    sub SPECIAL_EL () {
123      ADDRESS_EL |
124      BODY_EL |
125      DIV_EL |
126    
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
132      FORM_EL |
133      FRAMESET_EL |
134      HEADING_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240    };
241    
242    my $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $c1_entity_char = {  my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
400    
401    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
402      my $self = shift;      my (%opt) = @_;
403      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
404      ## TODO: if $charset is supported    };
405      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      ## Step 1        };
410      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
411        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444      }      }
445    
446      ## Step 2      ## Step 2
447      if (defined $self->{input_encoding} and      my $byte_buffer = '';
448          $self->{input_encoding} eq $charset) {      for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        return;        last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476      }      }
477    
478      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
479          ':'.$charset, level => 'w');      ## TODO: <meta charset>
480    
481      ## Step 3      ## Step 5
482      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
483    
484      ## Step 4      ## Step 6
485      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599    }; # $self->{change_encoding}    }; # $self->{change_encoding}
600    
601    my @args = @_; shift @args; # $s    my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
620      $s = \ (Encode::decode ($charset, $$bytes_s));      
621      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $return = $self->parse_char_string ($s, @args);  
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_string  } # parse_byte_stream
648    
649  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 655  sub parse_byte_string ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 175  sub parse_string ($$$;$) { Line 676  sub parse_string ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
681    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
682    my $line = 1;    $self->{column_prev} = -1;
683    my $column = 0;    $self->{column} = 0;
684    $self->{set_next_input_character} = sub {    $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_input_character}};      my $char = '';
688      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
697      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698      $column++;        if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709          } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_input_character} = [-1, -1, -1];  
740    $self->{next_input_character} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_input_character} = sub {      level => {must => 'm',
814      $self->{next_input_character} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 844  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
844  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
847  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
848  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
849  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
850  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 855  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
855  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
861  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 877  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
877  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
879  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
902  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 913  sub TABLE_IMS ()      { 0b1000000 }
913  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
914  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
915  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922    ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
924    ## NOTE: "after after body" insertion mode.
925  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
926    
927    ## NOTE: "after after frameset" insertion mode.
928  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
929    
930  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
931  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
932  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 319  sub IN_TABLE_IM () { TABLE_IMS } Line 940  sub IN_TABLE_IM () { TABLE_IMS }
940  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 327  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 949  sub IN_COLUMN_GROUP_IM () { 0b10 }
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
952      #$self->{s_kwd}; # state keyword - initialized when used
953      #$self->{entity__value}; # initialized when used
954      #$self->{entity__match}; # initialized when used
955    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
957    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
958    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
959    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
960    $self->{char} = [];    delete $self->{self_closing};
961    # $self->{next_input_character}    $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967    # $self->{escape}    # $self->{escape}
# Line 344  sub _initialize_tokenizer ($) { Line 972  sub _initialize_tokenizer ($) {
972  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
974  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##        ->{name}  ##        ->{name}
980  ##        ->{value}  ##        ->{value}
981  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
982  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 361  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
997  ## contains some requirements that are not detected by the  
998  ## parsing algorithm:  my $is_space = {
999  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1000  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1001  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1002  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1003  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1004  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1005    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026    
1027          if ($self->{nc} == 0x0026) { # &
1028            !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069              not $self->{escape}) {              not $self->{escape}) {
1070            $self->{state} = ENTITY_DATA_STATE;            !!!cp (1);
1071              ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081              !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1087              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1088                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1090                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1091              }              $self->{s_kwd} = '--';
1092                #
1093              } elsif ($self->{s_kwd} eq '---') {
1094                !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097              } else {
1098                !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105            if (length $self->{s_kwd}) {
1106              !!!cp (5.1);
1107              $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
1119              !!!cp (6);
1120            $self->{state} = TAG_OPEN_STATE;            $self->{state} = TAG_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } else {          } else {
1124              !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1132                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1133              delete $self->{escape};              delete $self->{escape};
1134              } else {
1135                !!!cp (9);
1136            }            }
1137            } else {
1138              !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!emit ({type => END_OF_FILE_TOKEN});          !!!cp (11);
1145            $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149          } else {
1150            !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
1154    
1155        # Anything else        # Anything else
1156        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1157                     data => chr $self->{next_input_character}};                     data => chr $self->{nc},
1158        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1159        !!!next-input-character;                    };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!emit ($token);                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          !!!emit ({type => CHARACTER_TOKEN, data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167            !!!cp (13);
1168            $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!emit ($token);          !!!cp (14);
1171            ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1179              !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            ## reconsume            !!!cp (16);
1189            $self->{state} = DATA_STATE;            #
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1190          }          }
1191    
1192            ## reconsume
1193            $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1201              !!!cp (17);
1202            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206              !!!cp (18);
1207            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            $self->{current_token}            !!!cp (19);
1213              $self->{ct}
1214              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1215                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            $self->{current_token} = {type => START_TAG_TOKEN,            !!!cp (20);
1224                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1225                                        tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1233              !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!parse-error (type => 'pio');            !!!cp (22);
1247              !!!parse-error (type => 'pio',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            ## $self->{next_input_character} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!parse-error (type => 'bare stago');            !!!cp (23);
1259              !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
# Line 517  sub _get_next_token ($) { Line 1273  sub _get_next_token ($) {
1273          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1277          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
1278    
1279                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281                redo A;          if (defined $self->{last_stag_name}) {
1282              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            }            $self->{s_kwd} = '';
1284            push @next_char, $self->{next_input_character};            ## Reconsume.
1285                    redo A;
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1286          } else {          } else {
1287            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1288            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289              !!!cp (28);
1290            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1291            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (29);
1302                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1303                = {type => END_TAG_TOKEN,
1304                   tag_name => chr ($self->{nc} + 0x0020),
1305                   line => $l, column => $c};
1306          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{nc} and
1310                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1311          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (30);
1312                            tag_name => chr ($self->{next_input_character})};          $self->{ct} = {type => END_TAG_TOKEN,
1313                                      tag_name => chr ($self->{nc}),
1314                                      line => $l, column => $c};
1315          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1316          !!!next-input-character;          !!!next-input-character;
1317          redo A;          redo A;
1318        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1319          !!!parse-error (type => 'empty end tag');          !!!cp (31);
1320            !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1327            !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338            !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343                                      column => $self->{column_prev} - 1,
1344                                     };
1345            ## NOTE: $self->{nc} is intentionally left as is.
1346            ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1406            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1407          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408          !!!next-input-character;          !!!next-input-character;
1409          redo A;          redo A;
1410        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1411          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412            $self->{current_token}->{first_start_tag}            !!!cp (35);
1413                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1415            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1417              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1418            }            #  !!! cp (36);
1419              #  !!! parse-error (type => 'end tag attribute');
1420              #} else {
1421                !!!cp (37);
1422              #}
1423          } else {          } else {
1424            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1425          }          }
1426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1427          !!!next-input-character;          !!!next-input-character;
1428    
1429          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1430    
1431          redo A;          redo A;
1432        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1435            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            $self->{current_token}->{first_start_tag}            !!!cp (39);
1444                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1446            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1448              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1449            }            #  !!! cp (40);
1450              #  !!! parse-error (type => 'end tag attribute');
1451              #} else {
1452                !!!cp (41);
1453              #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1467          redo A;          redo A;
1468        } else {        } else {
1469          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1470            $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1478            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            $self->{current_token}->{first_start_tag}            !!!cp (46);
1485                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1487            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1489                !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491              } else {
1492                !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1506                                value => ''};          $self->{ca}
1507                = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            $self->{current_token}->{first_start_tag}            !!!cp (52);
1522                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1526                !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1538    
1539          redo A;          redo A;
1540        } else {        } else {
# Line 748  sub _get_next_token ($) { Line 1542  sub _get_next_token ($) {
1542               0x0022 => 1, # "               0x0022 => 1, # "
1543               0x0027 => 1, # '               0x0027 => 1, # '
1544               0x003D => 1, # =               0x003D => 1, # =
1545              }->{$self->{next_input_character}}) {              }->{$self->{nc}}) {
1546              !!!cp (55);
1547            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1548            } else {
1549              !!!cp (56);
1550          }          }
1551          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{ca}
1552                                value => ''};              = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1564            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565          } else {            ## Discard $self->{ca} # MUST
1566            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1567              = $self->{current_attribute};            !!!cp (58);
1568              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569                = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1574            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580            !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            $self->{current_token}->{first_start_tag}            !!!cp (61);
1589                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {            !!!cp (62);
1592            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1608            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1617          redo A;          redo A;
1618        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            $self->{current_token}->{first_start_tag}            !!!cp (66);
1623                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1625            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1627                !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629              } else {
1630                ## NOTE: This state should never be reached.
1631                !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          if ($self->{next_input_character} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1644              $self->{next_input_character} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1645              !!!cp (69);
1646            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1647            } else {
1648              !!!cp (70);
1649          }          }
1650          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1657            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662            !!!cp (72);
1663          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            $self->{current_token}->{first_start_tag}            !!!cp (73);
1669                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1671            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1673                !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675              } else {
1676                ## NOTE: This state should never be reached.
1677                !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1691                                value => ''};          $self->{ca}
1692                = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            $self->{current_token}->{first_start_tag}            !!!cp (79);
1707                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1709            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1711                !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713              } else {
1714                ## NOTE: This state should never be reached.
1715                !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1728                                value => ''};              $self->{nc} == 0x0027) { # '
1729              !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1744            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749            !!!cp (84);
1750          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754            !!!cp (85);
1755          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759            !!!cp (86);
1760          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1765            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1767            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1771                !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773              } else {
1774                ## NOTE: This state should never be reached.
1775                !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            $self->{current_token}->{first_start_tag}            !!!cp (90);
1790                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1792            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1794                !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796              } else {
1797                ## NOTE: This state should never be reached.
1798                !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          if ($self->{next_input_character} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1811              !!!cp (93);
1812            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1813            } else {
1814              !!!cp (94);
1815          }          }
1816          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{ca}->{value} .= chr ($self->{nc});
1817          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823            !!!cp (95);
1824          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          $self->{last_attribute_value_state} = $self->{state};          !!!cp (96);
1829          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1830            ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            $self->{current_token}->{first_start_tag}            !!!cp (97);
1842                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1844            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1846                !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848              } else {
1849                ## NOTE: This state should never be reached.
1850                !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1863            $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874            !!!cp (101);
1875          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876          !!!next-input-character;          !!!next-input-character;
1877          redo A;          redo A;
1878        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1879          $self->{last_attribute_value_state} = $self->{state};          !!!cp (102);
1880          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1881            ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            $self->{current_token}->{first_start_tag}            !!!cp (103);
1893                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1895            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1897                !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1914            $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1925            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
           $self->{next_input_character} == 0x000B or # HT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1926          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927          !!!next-input-character;          !!!next-input-character;
1928          redo A;          redo A;
1929        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1930          $self->{last_attribute_value_state} = $self->{state};          !!!cp (108);
1931          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1932          !!!next-input-character;          ## "entity in attribute value state".  In this implementation, the
1933          redo A;          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934        } elsif ($self->{next_input_character} == 0x003E) { # >          ## implementation of the "consume a character reference" algorithm.
1935          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          $self->{entity_add} = -1;
1936            $self->{current_token}->{first_start_tag}          $self->{prev_state} = $self->{state};
1937                = not defined $self->{last_emitted_start_tag_name};          $self->{state} = ENTITY_STATE;
1938            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          !!!next-input-character;
1939          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          redo A;
1940          } elsif ($self->{nc} == 0x003E) { # >
1941            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942              !!!cp (109);
1943              $self->{last_stag_name} = $self->{ct}->{tag_name};
1944            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1947                !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949              } else {
1950                ## NOTE: This state should never be reached.
1951                !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            $self->{current_token}->{first_start_tag}            !!!cp (112);
1966                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1968            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1970                !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972              } else {
1973                ## NOTE: This state should never be reached.
1974                !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1983    
1984          redo A;          redo A;
1985        } else {        } else {
# Line 1133  sub _get_next_token ($) { Line 1987  sub _get_next_token ($) {
1987               0x0022 => 1, # "               0x0022 => 1, # "
1988               0x0027 => 1, # '               0x0027 => 1, # '
1989               0x003D => 1, # =               0x003D => 1, # =
1990              }->{$self->{next_input_character}}) {              }->{$self->{nc}}) {
1991              !!!cp (115);
1992            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1993            } else {
1994              !!!cp (116);
1995          }          }
1996          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
2005      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2007            $self->{next_input_character} == 0x000A or # LF          !!!cp (118);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2008          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009          !!!next-input-character;          !!!next-input-character;
2010          redo A;          redo A;
2011        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2012          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013            $self->{current_token}->{first_start_tag}            !!!cp (119);
2014                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2015            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
2016            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2018                !!!cp (120);
2019              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2020              } else {
2021                ## NOTE: This state should never be reached.
2022                !!!cp (121);
2023            }            }
2024          } else {          } else {
2025            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2026          }          }
2027          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2028          !!!next-input-character;          !!!next-input-character;
2029    
2030          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2031    
2032          redo A;          redo A;
2033        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036          !!!next-input-character;          !!!next-input-character;
2037          if ($self->{next_input_character} == 0x003E and # >          redo A;
2038              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2039              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2040            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041            #            !!!cp (122.3);
2042              $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051          } else {          } else {
2052            !!!parse-error (type => 'nestc');            die "$0: $self->{ct}->{type}: Unknown token type";
2053          }          }
2054          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2055          # next-input-character is already done          ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057          redo A;          redo A;
2058        } else {        } else {
2059            !!!cp ('124.1');
2060          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2061          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062          ## reconsume          ## reconsume
2063          redo A;          redo A;
2064        }        }
2065        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066          if ($self->{nc} == 0x003E) { # >
2067            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068              !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072              if ($self->{ct}->{attributes}) {
2073                !!!cp ('124.4');
2074                !!!parse-error (type => 'end tag attribute');
2075              } else {
2076                !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084            $self->{state} = DATA_STATE;
2085            !!!next-input-character;
2086    
2087            !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103            } else {
2104              die "$0: $self->{ct}->{type}: Unknown token type";
2105            }
2106            $self->{state} = DATA_STATE;
2107            ## Reconsume.
2108            !!!emit ($self->{ct}); # start tag or end tag
2109            redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       my $token = {type => COMMENT_TOKEN, data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
2120    
2121            !!!emit ($token);        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122          ## consumes characters one-by-one basis.
2123          
2124          if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127            !!!next-input-character;
2128    
2129            redo A;          !!!emit ($self->{ct}); # comment
2130          } elsif ($self->{next_input_character} == -1) {          redo A;
2131            $self->{state} = DATA_STATE;        } elsif ($self->{nc} == -1) {
2132            ## reconsume          !!!cp (125);
2133            $self->{state} = DATA_STATE;
2134            ## reconsume
2135    
2136            !!!emit ($token);          !!!emit ($self->{ct}); # comment
2137            redo A;
2138          } else {
2139            !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            redo A;          ## Stay in the state.
2146          } else {          !!!next-input-character;
2147            $token->{data} .= chr ($self->{next_input_character});          redo A;
2148            !!!next-input-character;        }
           redo BC;  
         }  
       } # BC  
2149      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
   
       my @next_char;  
       push @next_char, $self->{next_input_character};  
2151                
2152        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2153            !!!cp (133);
2154            $self->{state} = MD_HYPHEN_STATE;
2155          !!!next-input-character;          !!!next-input-character;
2156          push @next_char, $self->{next_input_character};          redo A;
2157          if ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2158            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};                 $self->{nc} == 0x0064) { # d
2159            $self->{state} = COMMENT_START_STATE;          ## ASCII case-insensitive.
2160            !!!next-input-character;          !!!cp (130);
2161            redo A;          $self->{state} = MD_DOCTYPE_STATE;
2162          }          $self->{s_kwd} = chr $self->{nc};
       } elsif ($self->{next_input_character} == 0x0044 or # D  
                $self->{next_input_character} == 0x0064) { # d  
2163          !!!next-input-character;          !!!next-input-character;
2164          push @next_char, $self->{next_input_character};          redo A;
2165          if ($self->{next_input_character} == 0x004F or # O        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166              $self->{next_input_character} == 0x006F) { # o                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167            !!!next-input-character;                 $self->{nc} == 0x005B) { # [
2168            push @next_char, $self->{next_input_character};          !!!cp (135.4);                
2169            if ($self->{next_input_character} == 0x0043 or # C          $self->{state} = MD_CDATA_STATE;
2170                $self->{next_input_character} == 0x0063) { # c          $self->{s_kwd} = '[';
2171              !!!next-input-character;          !!!next-input-character;
2172              push @next_char, $self->{next_input_character};          redo A;
2173              if ($self->{next_input_character} == 0x0054 or # T        } else {
2174                  $self->{next_input_character} == 0x0074) { # t          !!!cp (136);
               !!!next-input-character;  
               push @next_char, $self->{next_input_character};  
               if ($self->{next_input_character} == 0x0059 or # Y  
                   $self->{next_input_character} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_input_character};  
                 if ($self->{next_input_character} == 0x0050 or # P  
                     $self->{next_input_character} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_input_character};  
                   if ($self->{next_input_character} == 0x0045 or # E  
                       $self->{next_input_character} == 0x0065) { # e  
                     ## ISSUE: What a stupid code this is!  
                     $self->{state} = DOCTYPE_STATE;  
                     !!!next-input-character;  
                     redo A;  
                   }  
                 }  
               }  
             }  
           }  
         }  
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180          ## Reconsume.
2181        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2298        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2299            !!!cp (137);
2300          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2304            !!!cp (138);
2305          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2307          !!!next-input-character;          !!!next-input-character;
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2310    
2311          redo A;          redo A;
2312        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2313            !!!cp (139);
2314          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2315          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2316          ## reconsume          ## reconsume
2317    
2318          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2319    
2320          redo A;          redo A;
2321        } else {        } else {
2322          $self->{current_token}->{data} # comment          !!!cp (140);
2323              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2324                .= chr ($self->{nc});
2325          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2326          !!!next-input-character;          !!!next-input-character;
2327          redo A;          redo A;
2328        }        }
2329      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331            !!!cp (141);
2332          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2333          !!!next-input-character;          !!!next-input-character;
2334          redo A;          redo A;
2335        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336            !!!cp (142);
2337          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2338          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2339          !!!next-input-character;          !!!next-input-character;
2340    
2341          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2342    
2343          redo A;          redo A;
2344        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2345            !!!cp (143);
2346          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2347          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2348          ## reconsume          ## reconsume
2349    
2350          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2351    
2352          redo A;          redo A;
2353        } else {        } else {
2354          $self->{current_token}->{data} # comment          !!!cp (144);
2355              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2356                .= '-' . chr ($self->{nc});
2357          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        }        }
2361      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2362        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363            !!!cp (145);
2364          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2368            !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2378            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389            !!!cp (148);
2390          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2394            !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2404            $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411            !!!cp (151);
2412          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2420          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2421                            line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2428            !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2438          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2439                            line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2448            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2449          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2450          !!!next-input-character;          !!!next-input-character;
2451          redo A;          redo A;
2452        } else {        } else {
2453            !!!cp (156);
2454          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2455          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2456          ## reconsume          ## reconsume
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2461            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466            !!!cp (158);
2467          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2475            !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2478          ## reconsume          ## reconsume
2479    
2480          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          $self->{current_token}          !!!cp (160);
2485              = {type => DOCTYPE_TOKEN,          $self->{ct}->{name} = chr $self->{nc};
2486                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
                correct => 1};  
 ## ISSUE: "Set the token's name name to the" in the spec  
2487          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2494            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2495          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499            !!!cp (162);
2500          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507            !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token}->{name}          !!!cp (164);
2518            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2519              .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2526            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531            !!!cp (166);
2532          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2554              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2560        } else {        } else {
2561            !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568          }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604            $self->{state} = BOGUS_DOCTYPE_STATE;
2605            ## Reconsume.
2606            redo A;
2607        }        }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        $self->{state} = BOGUS_DOCTYPE_STATE;          ## Reconsume.
2645        # next-input-character is already done          redo A;
2646        redo A;        }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        if ({        if ($is_space->{$self->{nc}}) {
2649              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (181);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2655            $self->{ct}->{pubid} = ''; # DOCTYPE
2656          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2661            $self->{ct}->{pubid} = ''; # DOCTYPE
2662          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688            !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690            $self->{ct}->{quirks} = 1;
2691    
2692          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698            !!!cp (187);
2699          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2703            !!!cp (188);
2704          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708    
2709          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2710          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2726              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2727                .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737            !!!cp (191);
2738          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2746          !!!next-input-character;          !!!next-input-character;
2747    
2748          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2749          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2750    
2751          redo A;          redo A;
2752        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2765              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2766                .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
2776              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2782            $self->{ct}->{sysid} = ''; # DOCTYPE
2783          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2788            $self->{ct}->{sysid} = ''; # DOCTYPE
2789          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793            !!!cp (198);
2794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2801            !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812            !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814            $self->{ct}->{quirks} = 1;
2815    
2816          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
2822              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2828            $self->{ct}->{sysid} = ''; # DOCTYPE
2829          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2834            $self->{ct}->{sysid} = ''; # DOCTYPE
2835          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2849            !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860            !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862            $self->{ct}->{quirks} = 1;
2863    
2864          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870            !!!cp (207);
2871          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2875          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!cp (208);
2876            !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2898              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2899                .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909            !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!cp (212);
2915            !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2918          !!!next-input-character;          !!!next-input-character;
2919    
2920          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2925            !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2937              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2938                .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
2948              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953            !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2963          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2964          ## reconsume          ## reconsume
2965    
2966          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971            !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973            #$self->{ct}->{quirks} = 1;
2974    
2975          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981            !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998            !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3035    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042         $additional => 1,          !!!cp (221.5);
3043        }->{$self->{next_input_character}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      ## Don't consume          !!!next-input-character;
3045      ## No error          redo A;
3046      return undef;        } else {
3047    } elsif ($self->{next_input_character} == 0x0023) { # #          !!!cp (221.6);
3048      !!!next-input-character;          $self->{ct}->{data} .= ']';
3049      if ($self->{next_input_character} == 0x0078 or # x          $self->{state} = CDATA_SECTION_STATE;
3050          $self->{next_input_character} == 0x0058) { # X          ## Reconsume.
3051        my $code;          redo A;
3052        X: {        }
3053          my $x_char = $self->{next_input_character};      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          !!!next-input-character;        if ($self->{nc} == 0x003E) { # >
3055          if (0x0030 <= $self->{next_input_character} and          $self->{state} = DATA_STATE;
3056              $self->{next_input_character} <= 0x0039) { # 0..9          !!!next-input-character;
3057            $code ||= 0;          if (length $self->{ct}->{data}) { # character
3058            $code *= 0x10;            !!!cp (221.7);
3059            $code += $self->{next_input_character} - 0x0030;            !!!emit ($self->{ct}); # character
           redo X;  
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3060          } else {          } else {
3061            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063          }          }
3064            redo A;
3065          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3067            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3069            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3070            $code = 0xFFFD;          redo A;
3071          } elsif ($code == 0x000D) {        } else {
3072            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3073            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3074          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3075            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3076            $code = $c1_entity_char->{$code};          redo A;
3077          }        }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          return {type => CHARACTER_TOKEN, data => chr $code,        if ($is_space->{$self->{nc}} or
3080                  has_reference => 1};            {
3081        } # X              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082      } elsif (0x0030 <= $self->{next_input_character} and              $self->{entity_add} => 1,
3083               $self->{next_input_character} <= 0x0039) { # 0..9            }->{$self->{nc}}) {
3084        my $code = $self->{next_input_character} - 0x0030;          !!!cp (1001);
3085        !!!next-input-character;          ## Don't consume
3086                  ## No error
3087        while (0x0030 <= $self->{next_input_character} and          ## Return nothing.
3088                  $self->{next_input_character} <= 0x0039) { # 0..9          #
3089          $code *= 10;        } elsif ($self->{nc} == 0x0023) { # #
3090          $code += $self->{next_input_character} - 0x0030;          !!!cp (999);
3091                    $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105          !!!next-input-character;          !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181            !!!cp (1012);
3182            $self->{s_kwd} *= 10;
3183            $self->{s_kwd} += $self->{nc} - 0x0030;
3184            
3185            ## Stay in the state.
3186            !!!next-input-character;
3187            redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
3189            !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193            !!!cp (1014);
3194          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203            !!!cp (1015);
3204            !!!parse-error (type => 'invalid character reference',
3205                            text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208          } elsif ($code > 0x10FFFF) {
3209            !!!cp (1016);
3210            !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
3214          }
3215    
3216          if ($self->{prev_state} == DATA_STATE) {
3217            !!!cp (992);
3218            $self->{state} = $self->{prev_state};
3219            ## Reconsume.
3220            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221                      line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3317          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3322        }        }
3323          
3324        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3325      } else {          !!!cp (988);
3326        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3327        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3328        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329        return undef;                    line => $l, column => $c,
3330      }                   });
3331    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3332              $self->{next_input_character} <= 0x005A) or        } else {
3333             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3334              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3335      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3336      !!!next-input-character;          $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338      my $value = $entity_name;          redo A;
3339      my $match = 0;        }
3340      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3344             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3345             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3346               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3347              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3348               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3349              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3350               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3351              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3352        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3353        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3354          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3355            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356            $match = 1;              $self->{entity__match} = 1;
3357            !!!next-input-character;              !!!next-input-character;
3358            last;              #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367          } else {          } else {
3368            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3369            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3375        }        }
3376      }  
3377              my $data;
3378      if ($match > 0) {        my $has_ref;
3379        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($self->{entity__match} > 0) {
3380      } elsif ($match < 0) {          !!!cp (1023);
3381        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3382        if ($in_attr and $match < -1) {          $has_ref = 1;
3383          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396            }
3397        } else {        } else {
3398          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!cp (1026);
3399            !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value};  
3436      }      }
3437    } else {    } # A  
3438      ## no characters are consumed  
3439      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3440      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 2080  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2106  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474        
3475    !!!next-token;    !!!next-token;
3476    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
3482      ## NOTE: The "initial" insertion mode.
3483    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3484    
3485      ## NOTE: The "before html" insertion mode.
3486    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3487      $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489      ## NOTE: The "before head" insertion mode and so on.
3490    $self->_tree_construction_main;    $self->_tree_construction_main;
3491  } # _construct_tree  } # _construct_tree
3492    
3493  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3494    my $self = shift;    my $self = shift;
3495    
3496      ## NOTE: "initial" insertion mode
3497    
3498    INITIAL: {    INITIAL: {
3499      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3500        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 2126  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
3508            defined $token->{system_identifier}) {          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3512          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521          } else {
3522            !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3535                
3536        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537            !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3604            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3605            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3606            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3607            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
3610            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,            !!!cp ('t5');
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615                !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618                !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623              !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625            } else {
3626              !!!cp ('t9');
3627          }          }
3628          } else {
3629            !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638              !!!cp ('t11');
3639            } else {
3640              !!!cp ('t12');
3641          }          }
3642          } else {
3643            !!!cp ('t13');
3644        }        }
3645                
3646        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3647        !!!next-token;        !!!next-token;
3648        return;        return;
3649      } elsif ({      } elsif ({
# Line 2254  sub _tree_construction_initial ($) { Line 3651  sub _tree_construction_initial ($) {
3651                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
3652                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3655          !!!parse-error (type => 'no DOCTYPE', token => $token);
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
3666            ## Stay in the phase            !!!cp ('t15');
3667              ## Stay in the insertion mode.
3668            !!!next-token;            !!!next-token;
3669            redo INITIAL;            redo INITIAL;
3670            } else {
3671              !!!cp ('t16');
3672          }          }
3673          } else {
3674            !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
3681        return;        return;
3682      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3683          !!!cp ('t18');
3684        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3685        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3686                
3687        ## Stay in the phase.        ## Stay in the insertion mode.
3688        !!!next-token;        !!!next-token;
3689        redo INITIAL;        redo INITIAL;
3690      } else {      } else {
3691        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
3692      }      }
3693    } # INITIAL    } # INITIAL
3694    
3695      die "$0: _tree_construction_initial: This should be never reached";
3696  } # _tree_construction_initial  } # _tree_construction_initial
3697    
3698  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3699    my $self = shift;    my $self = shift;
3700    
3701      ## NOTE: "before html" insertion mode.
3702        
3703    B: {    B: {
3704        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3706            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the phase          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
3710          redo B;          redo B;
3711        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
3712            !!!cp ('t20');
3713          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3714          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3715          ## Stay in the phase          ## Stay in the insertion mode.
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
3723              ## Stay in the phase              !!!cp ('t21');
3724                ## Stay in the insertion mode.
3725              !!!next-token;              !!!next-token;
3726              redo B;              redo B;
3727              } else {
3728                !!!cp ('t22');
3729            }            }
3730            } else {
3731              !!!cp ('t23');
3732          }          }
3733    
3734          $self->{application_cache_selection}->(undef);          $self->{application_cache_selection}->(undef);
3735    
3736          #          #
3737        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3738          if ($token->{tag_name} eq 'html' and          if ($token->{tag_name} eq 'html') {
3739              $token->{attributes}->{manifest}) {            my $root_element;
3740            $self->{application_cache_selection}            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741                 ->($token->{attributes}->{manifest}->{value});            $self->{document}->append_child ($root_element);
3742            ## ISSUE: No relative reference resolution?            push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745              if ($token->{attributes}->{manifest}) {
3746                !!!cp ('t24');
3747                $self->{application_cache_selection}
3748                    ->($token->{attributes}->{manifest}->{value});
3749                ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753              } else {
3754                !!!cp ('t25');
3755                $self->{application_cache_selection}->(undef);
3756              }
3757    
3758              !!!nack ('t25c');
3759    
3760              !!!next-token;
3761              return; ## Go to the "before head" insertion mode.
3762          } else {          } else {
3763            $self->{application_cache_selection}->(undef);            !!!cp ('t25.1');
3764              #
3765          }          }
   
         ## ISSUE: There is an issue in the spec  
         #  
3766        } elsif ({        } elsif ({
3767                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
3768                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
3769                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3770          $self->{application_cache_selection}->(undef);          !!!cp ('t26');
   
         ## ISSUE: There is an issue in the spec  
3771          #          #
3772        } else {        } else {
3773          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775    
3776        my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3777        $self->{document}->append_child ($root_element);      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778        push @{$self->{open_elements}}, [$root_element, 'html'];      $self->{document}->append_child ($root_element);
3779        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780        #redo B;  
3781        return; ## Go to the main phase.      $self->{application_cache_selection}->(undef);
3782    
3783        ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785        return; ## Go to the "before head" insertion mode.
3786    } # B    } # B
3787    
3788      die "$0: _tree_construction_root_element: This should never be reached";
3789  } # _tree_construction_root_element  } # _tree_construction_root_element
3790    
3791  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2363  sub _reset_insertion_mode ($) { Line 3800  sub _reset_insertion_mode ($) {
3800            
3801      ## Step 3      ## Step 3
3802      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3803        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804          $last = 1;          $last = 1;
3805          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3806            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3807                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3808              #          } else {
3809            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3810          }          }
3811        }        }
3812              
3813        ## Step 4..13        ## Step 4..14
3814        my $new_mode = {        my $new_mode;
3815          if ($node->[1] & FOREIGN_EL) {
3816            !!!cp ('t28.1');
3817            ## NOTE: Strictly spaking, the line below only applies to MathML and
3818            ## SVG elements.  Currently the HTML syntax supports only MathML and
3819            ## SVG elements as foreigners.
3820            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821          } elsif ($node->[1] & TABLE_CELL_EL) {
3822            if ($last) {
3823              !!!cp ('t28.2');
3824              #
3825            } else {
3826              !!!cp ('t28.3');
3827              $new_mode = IN_CELL_IM;
3828            }
3829          } else {
3830            !!!cp ('t28.4');
3831            $new_mode = {
3832                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3833                        td => IN_CELL_IM,                        ## NOTE: |option| and |optgroup| do not set
3834                        th => IN_CELL_IM,                        ## insertion mode to "in select" by themselves.
3835                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3836                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3837                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2395  sub _reset_insertion_mode ($) { Line 3842  sub _reset_insertion_mode ($) {
3842                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3843                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3844                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3845                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3846          }
3847        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848                
3849        ## Step 14        ## Step 15
3850        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3851          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3852              !!!cp ('t29');
3853            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3854          } else {          } else {
3855              ## ISSUE: Can this state be reached?
3856              !!!cp ('t30');
3857            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3858          }          }
3859          return;          return;
3860          } else {
3861            !!!cp ('t31');
3862        }        }
3863                
3864        ## Step 15        ## Step 16
3865        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866                
3867        ## Step 16        ## Step 17
3868        $i--;        $i--;
3869        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3870                
3871        ## Step 17        ## Step 18
3872        redo S3;        redo S3;
3873      } # S3      } # S3
3874    
3875      die "$0: _reset_insertion_mode: This line should never be reached";
3876  } # _reset_insertion_mode  } # _reset_insertion_mode
3877    
3878  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2439  sub _tree_construction_main ($) { Line 3894  sub _tree_construction_main ($) {
3894      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3895      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3896        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3897            !!!cp ('t32');
3898          return;          return;
3899        }        }
3900      }      }
# Line 2453  sub _tree_construction_main ($) { Line 3909  sub _tree_construction_main ($) {
3909    
3910        ## Step 6        ## Step 6
3911        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3912            !!!cp ('t33_1');
3913          #          #
3914        } else {        } else {
3915          my $in_open_elements;          my $in_open_elements;
3916          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3917            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3918                !!!cp ('t33');
3919              $in_open_elements = 1;              $in_open_elements = 1;
3920              last OE;              last OE;
3921            }            }
3922          }          }
3923          if ($in_open_elements) {          if ($in_open_elements) {
3924              !!!cp ('t34');
3925            #            #
3926          } else {          } else {
3927              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3928              !!!cp ('t35');
3929            redo S4;            redo S4;
3930          }          }
3931        }        }
# Line 2487  sub _tree_construction_main ($) { Line 3948  sub _tree_construction_main ($) {
3948    
3949        ## Step 11        ## Step 11
3950        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3951            !!!cp ('t36');
3952          ## Step 7'          ## Step 7'
3953          $i++;          $i++;
3954          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3955                    
3956          redo S7;          redo S7;
3957        }        }
3958    
3959          !!!cp ('t37');
3960      } # S7      } # S7
3961    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3962    
3963    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3964      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3965        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3966            !!!cp ('t38');
3967          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3968          return;          return;
3969        }        }
3970      }      }
3971    
3972        !!!cp ('t39');
3973    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3974    
3975    my $parse_rcdata = sub ($$) {    my $insert;
3976      my ($content_model_flag, $insert) = @_;  
3977      my $parse_rcdata = sub ($) {
3978        my ($content_model_flag) = @_;
3979    
3980      ## Step 1      ## Step 1
3981      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3982      my $el;      my $el;
3983      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3984    
3985      ## Step 2      ## Step 2
3986      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3987    
3988      ## Step 3      ## Step 3
3989      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2522  sub _tree_construction_main ($) { Line 3991  sub _tree_construction_main ($) {
3991    
3992      ## Step 4      ## Step 4
3993      my $text = '';      my $text = '';
3994        !!!nack ('t40.1');
3995      !!!next-token;      !!!next-token;
3996      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3997          !!!cp ('t40');
3998        $text .= $token->{data};        $text .= $token->{data};
3999        !!!next-token;        !!!next-token;
4000      }      }
4001    
4002      ## Step 5      ## Step 5
4003      if (length $text) {      if (length $text) {
4004          !!!cp ('t41');
4005        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
4006        $el->append_child ($text);        $el->append_child ($text);
4007      }      }
# Line 2538  sub _tree_construction_main ($) { Line 4010  sub _tree_construction_main ($) {
4010      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
4011    
4012      ## Step 7      ## Step 7
4013      if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
4014            $token->{tag_name} eq $start_tag_name) {
4015          !!!cp ('t42');
4016        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4017      } else {      } else {
4018        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
4019          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4020            !!!cp ('t43');
4021            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4022          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4023            !!!cp ('t44');
4024            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4025          } else {
4026            die "$0: $content_model_flag in parse_rcdata";
4027          }
4028      }      }
4029      !!!next-token;      !!!next-token;
4030    }; # $parse_rcdata    }; # $parse_rcdata
4031    
4032    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4033      my $script_el;      my $script_el;
4034      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4035      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4036    
4037      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4038      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4039            
4040      my $text = '';      my $text = '';
4041        !!!nack ('t45.1');
4042      !!!next-token;      !!!next-token;
4043      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4044          !!!cp ('t45');
4045        $text .= $token->{data};        $text .= $token->{data};
4046        !!!next-token;        !!!next-token;
4047      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4048      if (length $text) {      if (length $text) {
4049          !!!cp ('t46');
4050        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4051      }      }
4052                                
# Line 2573  sub _tree_construction_main ($) { Line 4054  sub _tree_construction_main ($) {
4054    
4055      if ($token->{type} == END_TAG_TOKEN and      if ($token->{type} == END_TAG_TOKEN and
4056          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4057          !!!cp ('t47');
4058        ## Ignore the token        ## Ignore the token
4059      } else {      } else {
4060        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4061          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4062        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4063        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4064      }      }
4065            
4066      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4067          !!!cp ('t49');
4068        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4069      } else {      } else {
4070          !!!cp ('t50');
4071        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4072        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4073    
# Line 2596  sub _tree_construction_main ($) { Line 4081  sub _tree_construction_main ($) {
4081      !!!next-token;      !!!next-token;
4082    }; # $script_start_tag    }; # $script_start_tag
4083    
4084      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4085      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4086      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4087    
4088    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4089      my $tag_name = shift;      my $end_tag_token = shift;
4090        my $tag_name = $end_tag_token->{tag_name};
4091    
4092        ## NOTE: The adoption agency algorithm (AAA).
4093    
4094      FET: {      FET: {
4095        ## Step 1        ## Step 1
4096        my $formatting_element;        my $formatting_element;
4097        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4098        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4099          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4100              !!!cp ('t52');
4101              last AFE;
4102            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4103                         eq $tag_name) {
4104              !!!cp ('t51');
4105            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4106            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4107            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4108          }          }
4109        } # AFE        } # AFE
4110        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4111          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4112            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4113          ## Ignore the token          ## Ignore the token
4114          !!!next-token;          !!!next-token;
4115          return;          return;
# Line 2625  sub _tree_construction_main ($) { Line 4121  sub _tree_construction_main ($) {
4121          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4122          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4123            if ($in_scope) {            if ($in_scope) {
4124                !!!cp ('t54');
4125              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4126              last INSCOPE;              last INSCOPE;
4127            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4128              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4129                !!!parse-error (type => 'unmatched end tag',
4130                                text => $token->{tag_name},
4131                                token => $end_tag_token);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
4134              return;              return;
4135            }            }
4136          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4137                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4138            $in_scope = 0;            $in_scope = 0;
4139          }          }
4140        } # INSCOPE        } # INSCOPE
4141        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4142          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4143            !!!parse-error (type => 'unmatched end tag',
4144                            text => $token->{tag_name},
4145                            token => $end_tag_token);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4148          return;          return;
4149        }        }
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4152            !!!parse-error (type => 'not closed',
4153                            text => $self->{open_elements}->[-1]->[0]
4154                                ->manakai_local_name,
4155                            token => $end_tag_token);
4156        }        }
4157                
4158        ## Step 2        ## Step 2
# Line 2655  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4161        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4162          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4163          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4164              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4165              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4166               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4167              !!!cp ('t59');
4168            $furthest_block = $node;            $furthest_block = $node;
4169            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4170          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4171              !!!cp ('t60');
4172            last OE;            last OE;
4173          }          }
4174        } # OE        } # OE
4175                
4176        ## Step 3        ## Step 3
4177        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4178            !!!cp ('t61');
4179          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4180          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4181          !!!next-token;          !!!next-token;
# Line 2680  sub _tree_construction_main ($) { Line 4188  sub _tree_construction_main ($) {
4188        ## Step 5        ## Step 5
4189        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4190        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4191            !!!cp ('t62');
4192          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4193        }        }
4194                
# Line 2702  sub _tree_construction_main ($) { Line 4211  sub _tree_construction_main ($) {
4211          S7S2: {          S7S2: {
4212            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4213              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4214                  !!!cp ('t63');
4215                $node_i_in_active = $_;                $node_i_in_active = $_;
4216                last S7S2;                last S7S2;
4217              }              }
# Line 2715  sub _tree_construction_main ($) { Line 4225  sub _tree_construction_main ($) {
4225                    
4226          ## Step 4          ## Step 4
4227          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4228              !!!cp ('t64');
4229            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4230          }          }
4231                    
4232          ## Step 5          ## Step 5
4233          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4234              !!!cp ('t65');
4235            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4236            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4237            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2737  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } # S7          } # S7  
4250                
4251        ## Step 8        ## Step 8
4252        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4253            my $foster_parent_element;
4254            my $next_sibling;
4255            OE: for (reverse 0..$#{$self->{open_elements}}) {
4256              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4257                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4258                                 if (defined $parent and $parent->node_type == 1) {
4259                                   !!!cp ('t65.1');
4260                                   $foster_parent_element = $parent;
4261                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4262                                 } else {
4263                                   !!!cp ('t65.2');
4264                                   $foster_parent_element
4265                                     = $self->{open_elements}->[$_ - 1]->[0];
4266                                 }
4267                                 last OE;
4268                               }
4269                             } # OE
4270                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4271                               unless defined $foster_parent_element;
4272            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4273            $open_tables->[-1]->[1] = 1; # tainted
4274          } else {
4275            !!!cp ('t65.3');
4276            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4277          }
4278                
4279        ## Step 9        ## Step 9
4280        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2754  sub _tree_construction_main ($) { Line 4291  sub _tree_construction_main ($) {
4291        my $i;        my $i;
4292        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4293          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4294              !!!cp ('t66');
4295            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4296            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4297          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4298              !!!cp ('t67');
4299            $i = $_;            $i = $_;
4300          }          }
4301        } # AFE        } # AFE
# Line 2766  sub _tree_construction_main ($) { Line 4305  sub _tree_construction_main ($) {
4305        undef $i;        undef $i;
4306        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4307          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4308              !!!cp ('t68');
4309            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4310            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4311          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4312              !!!cp ('t69');
4313            $i = $_;            $i = $_;
4314          }          }
4315        } # OE        } # OE
# Line 2779  sub _tree_construction_main ($) { Line 4320  sub _tree_construction_main ($) {
4320      } # FET      } # FET
4321    }; # $formatting_end_tag    }; # $formatting_end_tag
4322    
4323    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4324      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4325    }; # $insert_to_current    }; # $insert_to_current
4326    
4327    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4328                         my $child = shift;      my $child = shift;
4329                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4330                              table => 1, tbody => 1, tfoot => 1,        # MUST
4331                              thead => 1, tr => 1,        my $foster_parent_element;
4332                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4333                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4334                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
4335                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4336                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4337                                   !!!cp ('t70');
4338                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4339                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4340                               } else {                               } else {
4341                                   !!!cp ('t71');
4342                                 $foster_parent_element                                 $foster_parent_element
4343                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4344                               }                               }
# Line 2809  sub _tree_construction_main ($) { Line 4349  sub _tree_construction_main ($) {
4349                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4350                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4351                             ($child, $next_sibling);                             ($child, $next_sibling);
4352                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4353                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4354                         }        !!!cp ('t72');
4355          $self->{open_elements}->[-1]->[0]->append_child ($child);
4356        }
4357    }; # $insert_to_foster    }; # $insert_to_foster
4358    
4359    my $insert;    B: while (1) {
   
   B: {  
4360      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4361        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
4362          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363        ## Ignore the token        ## Ignore the token
4364        ## Stay in the phase        ## Stay in the phase
4365        !!!next-token;        !!!next-token;
4366        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         #  
       } else {  
         ## 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_TOKEN, tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
4367      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4368               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4369        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370          ## Turn into the main phase          !!!cp ('t79');
4371          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4373        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          ## Turn into the main phase          !!!cp ('t80');
4375          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377          } else {
4378            !!!cp ('t81');
4379        }        }
4380    
4381  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
4382  ## ISSUE: "<html>" in fragment is not a parse error.        !!!parse-error (type => 'not first start tag', token => $token);
       unless ($token->{first_start_tag}) {  
         !!!parse-error (type => 'not first start tag');  
       }  
4383        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4384        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4385          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4386              !!!cp ('t84');
4387            $top_el->set_attribute_ns            $top_el->set_attribute_ns
4388              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
4389               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4390          }          }
4391        }        }
4392          !!!nack ('t84.1');
4393        !!!next-token;        !!!next-token;
4394        redo B;        next B;
4395      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4396        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4397        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4398            !!!cp ('t85');
4399          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
4400        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4401            !!!cp ('t86');
4402          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
4403        } else {        } else {
4404            !!!cp ('t87');
4405          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4406        }        }
4407        !!!next-token;        !!!next-token;
4408        redo B;        next B;
4409      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4410        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4411          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4412            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4413            !!!next-token;
4414            next B;
4415          } elsif ($token->{type} == START_TAG_TOKEN) {
4416            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4417                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4418                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4419                ($token->{tag_name} eq 'svg' and
4420                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4421              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4422              !!!cp ('t87.2');
4423              #
4424            } elsif ({
4425                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4426                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4427                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4428                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4429                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4430                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4431                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4432                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4433                     }->{$token->{tag_name}}) {
4434              !!!cp ('t87.2');
4435              !!!parse-error (type => 'not closed',
4436                              text => $self->{open_elements}->[-1]->[0]
4437                                  ->manakai_local_name,
4438                              token => $token);
4439    
4440              pop @{$self->{open_elements}}
4441                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4442    
4443              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4444              ## Reprocess.
4445              next B;
4446            } else {
4447              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4448              my $tag_name = $token->{tag_name};
4449              if ($nsuri eq $SVG_NS) {
4450                $tag_name = {
4451                   altglyph => 'altGlyph',
4452                   altglyphdef => 'altGlyphDef',
4453                   altglyphitem => 'altGlyphItem',
4454                   animatecolor => 'animateColor',
4455                   animatemotion => 'animateMotion',
4456                   animatetransform => 'animateTransform',
4457                   clippath => 'clipPath',
4458                   feblend => 'feBlend',
4459                   fecolormatrix => 'feColorMatrix',
4460                   fecomponenttransfer => 'feComponentTransfer',
4461                   fecomposite => 'feComposite',
4462                   feconvolvematrix => 'feConvolveMatrix',
4463                   fediffuselighting => 'feDiffuseLighting',
4464                   fedisplacementmap => 'feDisplacementMap',
4465                   fedistantlight => 'feDistantLight',
4466                   feflood => 'feFlood',
4467                   fefunca => 'feFuncA',
4468                   fefuncb => 'feFuncB',
4469                   fefuncg => 'feFuncG',
4470                   fefuncr => 'feFuncR',
4471                   fegaussianblur => 'feGaussianBlur',
4472                   feimage => 'feImage',
4473                   femerge => 'feMerge',
4474                   femergenode => 'feMergeNode',
4475                   femorphology => 'feMorphology',
4476                   feoffset => 'feOffset',
4477                   fepointlight => 'fePointLight',
4478                   fespecularlighting => 'feSpecularLighting',
4479                   fespotlight => 'feSpotLight',
4480                   fetile => 'feTile',
4481                   feturbulence => 'feTurbulence',
4482                   foreignobject => 'foreignObject',
4483                   glyphref => 'glyphRef',
4484                   lineargradient => 'linearGradient',
4485                   radialgradient => 'radialGradient',
4486                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4487                   textpath => 'textPath',  
4488                }->{$tag_name} || $tag_name;
4489              }
4490    
4491              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4492    
4493              ## "adjust foreign attributes" - done in insert-element-f
4494    
4495              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4496    
4497              if ($self->{self_closing}) {
4498                pop @{$self->{open_elements}};
4499                !!!ack ('t87.3');
4500              } else {
4501                !!!cp ('t87.4');
4502              }
4503    
4504              !!!next-token;
4505              next B;
4506            }
4507          } elsif ($token->{type} == END_TAG_TOKEN) {
4508            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4509            !!!cp ('t87.5');
4510            #
4511          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4512            !!!cp ('t87.6');
4513            !!!parse-error (type => 'not closed',
4514                            text => $self->{open_elements}->[-1]->[0]
4515                                ->manakai_local_name,
4516                            token => $token);
4517    
4518            pop @{$self->{open_elements}}
4519                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4520    
4521            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4522    
4523            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524            ## Reprocess.
4525            next B;
4526          } else {
4527            die "$0: $token->{type}: Unknown token type";        
4528          }
4529        }
4530    
4531        if ($self->{insertion_mode} & HEAD_IMS) {
4532          if ($token->{type} == CHARACTER_TOKEN) {
4533            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535                !!!cp ('t88.2');
4536                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537                #
4538              } else {
4539                !!!cp ('t88.1');
4540                ## Ignore the token.
4541                #
4542              }
4543            unless (length $token->{data}) {            unless (length $token->{data}) {
4544                !!!cp ('t88');
4545              !!!next-token;              !!!next-token;
4546              redo B;              next B;
4547            }            }
4548    ## TODO: set $token->{column} appropriately
4549          }          }
4550    
4551          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552              !!!cp ('t89');
4553            ## As if <head>            ## As if <head>
4554            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4555            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4556            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4557                  [$self->{head_element}, $el_category->{head}];
4558    
4559            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4560            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4561    
4562            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4563          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4564              !!!cp ('t90');
4565            ## As if </noscript>            ## As if </noscript>
4566            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4567            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4568                        
4569            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4570            ## As if </head>            ## As if </head>
# Line 2920  sub _tree_construction_main ($) { Line 4572  sub _tree_construction_main ($) {
4572    
4573            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4574          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4575              !!!cp ('t91');
4576            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4577    
4578            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4579            } else {
4580              !!!cp ('t92');
4581          }          }
4582    
4583              ## "after head" insertion mode          ## "after head" insertion mode
4584              ## As if <body>          ## As if <body>
4585              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4586              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4587              ## reprocess          ## reprocess
4588              redo B;          next B;
4589            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4590              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4591                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4592                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!cp ('t93');
4593                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4594                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];              $self->{open_elements}->[-1]->[0]->append_child
4595                  $self->{insertion_mode} = IN_HEAD_IM;                  ($self->{head_element});
4596                  !!!next-token;              push @{$self->{open_elements}},
4597                  redo B;                  [$self->{head_element}, $el_category->{head}];
4598                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4599                  #              !!!nack ('t93.1');
4600                } else {              !!!next-token;
4601                  !!!parse-error (type => 'in head:head'); # or in head noscript              next B;
4602                  ## Ignore the token            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603                  !!!next-token;              !!!cp ('t93.2');
4604                  redo B;              !!!parse-error (type => 'after head', text => 'head',
4605                }                              token => $token);
4606              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              ## Ignore the token
4607                ## As if <head>              !!!nack ('t93.3');
4608                !!!create-element ($self->{head_element}, 'head');              !!!next-token;
4609                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              next B;
4610                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4611                !!!cp ('t95');
4612                !!!parse-error (type => 'in head:head',
4613                                token => $token); # or in head noscript
4614                ## Ignore the token
4615                !!!nack ('t95.1');
4616                !!!next-token;
4617                next B;
4618              }
4619            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620              !!!cp ('t96');
4621              ## As if <head>
4622              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4623              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4624              push @{$self->{open_elements}},
4625                  [$self->{head_element}, $el_category->{head}];
4626    
4627                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4628                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4629              }          } else {
4630              !!!cp ('t97');
4631            }
4632    
4633              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
4634                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4635                    !!!cp ('t98');
4636                  ## As if </noscript>                  ## As if </noscript>
4637                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4638                  !!!parse-error (type => 'in noscript:base');                  !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                                
4641                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4642                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4643                  } else {
4644                    !!!cp ('t99');
4645                }                }
4646    
4647                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4648                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t100');
4650                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4651                }                                  text => $token->{tag_name}, token => $token);
4652                !!!insert-element ($token->{tag_name}, $token->{attributes});                  push @{$self->{open_elements}},
4653                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                      [$self->{head_element}, $el_category->{head}];
4654                pop @{$self->{open_elements}}                } else {
4655                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  !!!cp ('t101');
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'link') {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} == AFTER_HEAD_IM) {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
4656                }                }
4657                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4658                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}};
4659                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4660                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661                  !!!nack ('t101.1');
4662                !!!next-token;                !!!next-token;
4663                redo B;                next B;
4664            } elsif ($token->{tag_name} eq 'link') {
4665              ## NOTE: There is a "as if in head" code clone.
4666              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                !!!cp ('t102');
4668                !!!parse-error (type => 'after head',
4669                                text => $token->{tag_name}, token => $token);
4670                push @{$self->{open_elements}},
4671                    [$self->{head_element}, $el_category->{head}];
4672              } else {
4673                !!!cp ('t103');
4674              }
4675              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4676              pop @{$self->{open_elements}};
4677              pop @{$self->{open_elements}} # <head>
4678                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4679              !!!ack ('t103.1');
4680              !!!next-token;
4681              next B;
4682            } elsif ($token->{tag_name} eq 'command' or
4683                     $token->{tag_name} eq 'eventsource') {
4684              if ($self->{insertion_mode} == IN_HEAD_IM) {
4685                ## NOTE: If the insertion mode at the time of the emission
4686                ## of the token was "before head", $self->{insertion_mode}
4687                ## is already changed to |IN_HEAD_IM|.
4688    
4689                ## NOTE: There is a "as if in head" code clone.
4690                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4691                pop @{$self->{open_elements}};
4692                pop @{$self->{open_elements}} # <head>
4693                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4694                !!!ack ('t103.2');
4695                !!!next-token;
4696                next B;
4697              } else {
4698                ## NOTE: "in head noscript" or "after head" insertion mode
4699                ## - in these cases, these tags are treated as same as
4700                ## normal in-body tags.
4701                !!!cp ('t103.3');
4702                #
4703              }
4704              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4705                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4706                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4707                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t104');
4708                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4709                                    text => $token->{tag_name}, token => $token);
4710                    push @{$self->{open_elements}},
4711                        [$self->{head_element}, $el_category->{head}];
4712                  } else {
4713                    !!!cp ('t105');
4714                }                }
4715                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4716                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}};
4717    
4718                unless ($self->{confident}) {                unless ($self->{confident}) {
4719                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4720                      !!!cp ('t106');
4721                      ## NOTE: Whether the encoding is supported or not is handled
4722                      ## in the {change_encoding} callback.
4723                    $self->{change_encoding}                    $self->{change_encoding}
4724                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4725                             $token);
4726                                        
4727                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4728                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4729                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4730                                                 ->{has_reference});                                                 ->{has_reference});
4731                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4732                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4733                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4734                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4735                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4736                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4737                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4738                        !!!cp ('t107');
4739                        ## NOTE: Whether the encoding is supported or not is handled
4740                        ## in the {change_encoding} callback.
4741                      $self->{change_encoding}                      $self->{change_encoding}
4742                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4743                               $token);
4744                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4745                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4746                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
4747                                                     ->{has_reference});                                                     ->{has_reference});
4748                      } else {
4749                        !!!cp ('t108');
4750                    }                    }
4751                  }                  }
4752                } else {                } else {
4753                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
4754                      !!!cp ('t109');
4755                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4756                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4757                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4758                                                 ->{has_reference});                                                 ->{has_reference});
4759                  }                  }
4760                  if ($token->{attributes}->{content}) {                  if ($token->{attributes}->{content}) {
4761                      !!!cp ('t110');
4762                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4763                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4764                                             $token->{attributes}->{content}                                             $token->{attributes}->{content}
# Line 3039  sub _tree_construction_main ($) { Line 4766  sub _tree_construction_main ($) {
4766                  }                  }
4767                }                }
4768    
4769                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4770                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4771                  !!!ack ('t110.1');
4772                !!!next-token;                !!!next-token;
4773                redo B;                next B;
4774              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
4775                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4776                    !!!cp ('t111');
4777                  ## As if </noscript>                  ## As if </noscript>
4778                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4779                  !!!parse-error (type => 'in noscript:title');                  !!!parse-error (type => 'in noscript', text => 'title',
4780                                    token => $token);
4781                                
4782                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4783                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4784                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t112');
4786                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4787                                    text => $token->{tag_name}, token => $token);
4788                    push @{$self->{open_elements}},
4789                        [$self->{head_element}, $el_category->{head}];
4790                  } else {
4791                    !!!cp ('t113');
4792                }                }
4793    
4794                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4795                my $parent = defined $self->{head_element} ? $self->{head_element}                my $parent = defined $self->{head_element} ? $self->{head_element}
4796                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
4797                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4798                                sub { $parent->append_child ($_[0]) });                pop @{$self->{open_elements}} # <head>
               pop @{$self->{open_elements}}  
4799                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4800                redo B;                next B;
4801              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4802                         $token->{tag_name} eq 'noframes') {
4803                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4804                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4805                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4806                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4807                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
4808                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4809                                    text => $token->{tag_name}, token => $token);
4810                    push @{$self->{open_elements}},
4811                        [$self->{head_element}, $el_category->{head}];
4812                  } else {
4813                    !!!cp ('t115');
4814                }                }
4815                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4816                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4817                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4818                redo B;                next B;
4819              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4820                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4821                    !!!cp ('t116');
4822                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4823                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4824                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4825                    !!!nack ('t116.1');
4826                  !!!next-token;                  !!!next-token;
4827                  redo B;                  next B;
4828                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4829                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4830                    !!!parse-error (type => 'in noscript', text => 'noscript',
4831                                    token => $token);
4832                  ## Ignore the token                  ## Ignore the token
4833                    !!!nack ('t117.1');
4834                  !!!next-token;                  !!!next-token;
4835                  redo B;                  next B;
4836                } else {                } else {
4837                    !!!cp ('t118');
4838                  #                  #
4839                }                }
4840              } elsif ($token->{tag_name} eq 'script') {              } elsif ($token->{tag_name} eq 'script') {
4841                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4842                    !!!cp ('t119');
4843                  ## As if </noscript>                  ## As if </noscript>
4844                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4845                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript', text => 'script',
4846                                    token => $token);
4847                                
4848                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4849                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4850                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4851                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t120');
4852                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4853                                    text => $token->{tag_name}, token => $token);
4854                    push @{$self->{open_elements}},
4855                        [$self->{head_element}, $el_category->{head}];
4856                  } else {
4857                    !!!cp ('t121');
4858                }                }
4859    
4860                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4861                $script_start_tag->($insert_to_current);                $script_start_tag->();
4862                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4863                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4864                redo B;                next B;
4865              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4866                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4867                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4868                    !!!cp ('t122');
4869                  ## As if </noscript>                  ## As if </noscript>
4870                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4871                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4872                                    text => $token->{tag_name}, token => $token);
4873                                    
4874                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4875                  ## As if </head>                  ## As if </head>
# Line 3122  sub _tree_construction_main ($) { Line 4877  sub _tree_construction_main ($) {
4877                                    
4878                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4879                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4880                    !!!cp ('t124');
4881                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4882                                    
4883                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4884                  } else {
4885                    !!!cp ('t125');
4886                }                }
4887    
4888                ## "after head" insertion mode                ## "after head" insertion mode
4889                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4890                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4891                    !!!cp ('t126');
4892                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
4893                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
4894                    !!!cp ('t127');
4895                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4896                } else {                } else {
4897                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4898                }                }
4899                  !!!nack ('t127.1');
4900                !!!next-token;                !!!next-token;
4901                redo B;                next B;
4902              } else {              } else {
4903                  !!!cp ('t128');
4904                #                #
4905              }              }
4906    
4907              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4908                  !!!cp ('t129');
4909                ## As if </noscript>                ## As if </noscript>
4910                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4911                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4912                                  text => $token->{tag_name}, token => $token);
4913                                
4914                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4915                ## As if </head>                ## As if </head>
# Line 3153  sub _tree_construction_main ($) { Line 4917  sub _tree_construction_main ($) {
4917    
4918                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4919              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4920                  !!!cp ('t130');
4921                ## As if </head>                ## As if </head>
4922                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4923    
4924                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4925                } else {
4926                  !!!cp ('t131');
4927              }              }
4928    
4929              ## "after head" insertion mode              ## "after head" insertion mode
4930              ## As if <body>              ## As if <body>
4931              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4932              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4933              ## reprocess              ## reprocess
4934              redo B;              !!!ack-later;
4935                next B;
4936            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4937              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4938                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4939                    !!!cp ('t132');
4940                  ## As if <head>                  ## As if <head>
4941                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4942                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4943                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4944                        [$self->{head_element}, $el_category->{head}];
4945    
4946                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4947                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4948                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4949                  !!!next-token;                  !!!next-token;
4950                  redo B;                  next B;
4951                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4952                    !!!cp ('t133');
4953                  ## As if </noscript>                  ## As if </noscript>
4954                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4955                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/',
4956                                    text => 'head', token => $token);
4957                                    
4958                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4959                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4960                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4961                  !!!next-token;                  !!!next-token;
4962                  redo B;                  next B;
4963                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4964                    !!!cp ('t134');
4965                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4966                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4967                  !!!next-token;                  !!!next-token;
4968                  redo B;                  next B;
4969                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4970                    !!!cp ('t134.1');
4971                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4972                                    token => $token);
4973                    ## Ignore the token
4974                    !!!next-token;
4975                    next B;
4976                } else {                } else {
4977                  #                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
4978                }                }
4979              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4980                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4981                    !!!cp ('t136');
4982                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4983                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4984                  !!!next-token;                  !!!next-token;
4985                  redo B;                  next B;
4986                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4987                  !!!parse-error (type => 'unmatched end tag:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
4988                    !!!cp ('t137');
4989                    !!!parse-error (type => 'unmatched end tag',
4990                                    text => 'noscript', token => $token);
4991                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4992                  !!!next-token;                  !!!next-token;
4993                  redo B;                  next B;
4994                } else {                } else {
4995                    !!!cp ('t138');
4996                  #                  #
4997                }                }
4998              } elsif ({              } elsif ({
4999                        body => 1, html => 1,                        body => 1, html => 1,
5000                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5001                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5002                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5003                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5004                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  !!!cp ('t140');
5005                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'unmatched end tag',
5006                                    text => $token->{tag_name}, token => $token);
5007                  $self->{insertion_mode} = IN_HEAD_IM;                  ## Ignore the token
5008                  ## Reprocess in the "in head" insertion mode...                  !!!next-token;
5009                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                  next B;
5010                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5011                    !!!cp ('t140.1');
5012                    !!!parse-error (type => 'unmatched end tag',
5013                                    text => $token->{tag_name}, token => $token);
5014                  ## Ignore the token                  ## Ignore the token
5015                  !!!next-token;                  !!!next-token;
5016                  redo B;                  next B;
5017                  } else {
5018                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5019                }                }
5020                              } elsif ($token->{tag_name} eq 'p') {
5021                #                !!!cp ('t142');
5022              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5023                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5024                       }->{$token->{tag_name}}) {                ## Ignore the token
5025                  !!!next-token;
5026                  next B;
5027                } elsif ($token->{tag_name} eq 'br') {
5028                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5029                  ## As if <head>                  !!!cp ('t142.2');
5030                  !!!create-element ($self->{head_element}, 'head');                  ## (before head) as if <head>, (in head) as if </head>
5031                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5032                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5033                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5034      
5035                    ## Reprocess in the "after head" insertion mode...
5036                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5037                    !!!cp ('t143.2');
5038                    ## As if </head>
5039                    pop @{$self->{open_elements}};
5040                    $self->{insertion_mode} = AFTER_HEAD_IM;
5041      
5042                    ## Reprocess in the "after head" insertion mode...
5043                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5044                    !!!cp ('t143.3');
5045                    ## ISSUE: Two parse errors for <head><noscript></br>
5046                    !!!parse-error (type => 'unmatched end tag',
5047                                    text => 'br', token => $token);
5048                    ## As if </noscript>
5049                    pop @{$self->{open_elements}};
5050                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5051    
5052                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5053                }                  ## As if </head>
5054                    pop @{$self->{open_elements}};
5055                    $self->{insertion_mode} = AFTER_HEAD_IM;
5056    
5057                #                  ## Reprocess in the "after head" insertion mode...
5058              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5059                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
5060                  #                  #
5061                } else {                } else {
5062                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5063                }                }
5064    
5065                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5066                  !!!parse-error (type => 'unmatched end tag',
5067                                  text => 'br', token => $token);
5068                  ## Ignore the token
5069                  !!!next-token;
5070                  next B;
5071                } else {
5072                  !!!cp ('t145');
5073                  !!!parse-error (type => 'unmatched end tag',
5074                                  text => $token->{tag_name}, token => $token);
5075                  ## Ignore the token
5076                  !!!next-token;
5077                  next B;
5078              }              }
5079    
5080              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5081                  !!!cp ('t146');
5082                ## As if </noscript>                ## As if </noscript>
5083                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5084                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5085                                  text => $token->{tag_name}, token => $token);
5086                                
5087                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5088                ## As if </head>                ## As if </head>
# Line 3265  sub _tree_construction_main ($) { Line 5090  sub _tree_construction_main ($) {
5090    
5091                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5092              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5093                  !!!cp ('t147');
5094                ## As if </head>                ## As if </head>
5095                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5096    
5097                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5098              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5099                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
5100                  !!!cp ('t148');
5101                  !!!parse-error (type => 'unmatched end tag',
5102                                  text => $token->{tag_name}, token => $token);
5103                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5104                !!!next-token;                !!!next-token;
5105                redo B;                next B;
5106                } else {
5107                  !!!cp ('t149');
5108              }              }
5109    
5110              ## "after head" insertion mode              ## "after head" insertion mode
5111              ## As if <body>              ## As if <body>
5112              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5113              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5114              ## reprocess              ## reprocess
5115              redo B;              next B;
5116            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5117              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5118            }            !!!cp ('t149.1');
5119    
5120              ## NOTE: As if <head>
5121              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5122              $self->{open_elements}->[-1]->[0]->append_child
5123                  ($self->{head_element});
5124              #push @{$self->{open_elements}},
5125              #    [$self->{head_element}, $el_category->{head}];
5126              #$self->{insertion_mode} = IN_HEAD_IM;
5127              ## NOTE: Reprocess.
5128    
5129              ## NOTE: As if </head>
5130              #pop @{$self->{open_elements}};
5131              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5132              ## NOTE: Reprocess.
5133              
5134              #
5135            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5136              !!!cp ('t149.2');
5137    
5138            ## ISSUE: An issue in the spec.            ## NOTE: As if </head>
5139              pop @{$self->{open_elements}};
5140              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5141              ## NOTE: Reprocess.
5142    
5143              #
5144            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5145              !!!cp ('t149.3');
5146    
5147              !!!parse-error (type => 'in noscript:#eof', token => $token);
5148    
5149              ## As if </noscript>
5150              pop @{$self->{open_elements}};
5151              #$self->{insertion_mode} = IN_HEAD_IM;
5152              ## NOTE: Reprocess.
5153    
5154              ## NOTE: As if </head>
5155              pop @{$self->{open_elements}};
5156              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5157              ## NOTE: Reprocess.
5158    
5159              #
5160            } else {
5161              !!!cp ('t149.4');
5162              #
5163            }
5164    
5165            ## NOTE: As if <body>
5166            !!!insert-element ('body',, $token);
5167            $self->{insertion_mode} = IN_BODY_IM;
5168            ## NOTE: Reprocess.
5169            next B;
5170          } else {
5171            die "$0: $token->{type}: Unknown token type";
5172          }
5173      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5174            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5175                !!!cp ('t150');
5176              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5177              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5178                            
5179              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5180    
5181              !!!next-token;              !!!next-token;
5182              redo B;              next B;
5183            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5184              if ({              if ({
5185                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3303  sub _tree_construction_main ($) { Line 5187  sub _tree_construction_main ($) {
5187                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5188                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5189                  ## have an element in table scope                  ## have an element in table scope
5190                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5191                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5192                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5193                      $tn = $node->[1];                      !!!cp ('t151');
5194                      last INSCOPE;  
5195                    } elsif ({                      ## Close the cell
5196                              table => 1, html => 1,                      !!!back-token; # <x>
5197                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5198                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5199                    }                                line => $token->{line},
5200                  } # INSCOPE                                column => $token->{column}};
5201                    unless (defined $tn) {                      next B;
5202                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5203                      ## Ignore the token                      !!!cp ('t152');
5204                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5205                      redo B;                      last;
5206                    }                    }
5207                                    }
5208                  ## Close the cell  
5209                  !!!back-token; # <?>                  !!!cp ('t153');
5210                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
5211                  redo B;                      text => $token->{tag_name}, token => $token);
5212                    ## Ignore the token
5213                    !!!nack ('t153.1');
5214                    !!!next-token;
5215                    next B;
5216                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5217                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5218                                    token => $token);
5219                                    
5220                  ## As if </caption>                  ## NOTE: As if </caption>.
5221                  ## have a table element in table scope                  ## have a table element in table scope
5222                  my $i;                  my $i;
5223                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5224                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5225                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5226                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5227                      last INSCOPE;                        !!!cp ('t155');
5228                    } elsif ({                        $i = $_;
5229                              table => 1, html => 1,                        last INSCOPE;
5230                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5231                      last INSCOPE;                        !!!cp ('t156');
5232                          last;
5233                        }
5234                    }                    }
5235    
5236                      !!!cp ('t157');
5237                      !!!parse-error (type => 'start tag not allowed',
5238                                      text => $token->{tag_name}, token => $token);
5239                      ## Ignore the token
5240                      !!!nack ('t157.1');
5241                      !!!next-token;
5242                      next B;
5243                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5244                                    
5245                  ## generate implied end tags                  ## generate implied end tags
5246                  if ({                  while ($self->{open_elements}->[-1]->[1]
5247                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5248                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5249                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token; # <?>  
                   $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5250                  }                  }
5251    
5252                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5253                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5254                      !!!parse-error (type => 'not closed',
5255                                      text => $self->{open_elements}->[-1]->[0]
5256                                          ->manakai_local_name,
5257                                      token => $token);
5258                    } else {
5259                      !!!cp ('t160');
5260                  }                  }
5261                                    
5262                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3375  sub _tree_construction_main ($) { Line 5266  sub _tree_construction_main ($) {
5266                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5267                                    
5268                  ## reprocess                  ## reprocess
5269                  redo B;                  !!!ack-later;
5270                    next B;
5271                } else {                } else {
5272                    !!!cp ('t161');
5273                  #                  #
5274                }                }
5275              } else {              } else {
5276                  !!!cp ('t162');
5277                #                #
5278              }              }
5279            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3389  sub _tree_construction_main ($) { Line 5283  sub _tree_construction_main ($) {
5283                  my $i;                  my $i;
5284                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5285                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5286                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5287                        !!!cp ('t163');
5288                      $i = $_;                      $i = $_;
5289                      last INSCOPE;                      last INSCOPE;
5290                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5291                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5292                      last INSCOPE;                      last INSCOPE;
5293                    }                    }
5294                  } # INSCOPE                  } # INSCOPE
5295                    unless (defined $i) {                    unless (defined $i) {
5296                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5297                        !!!parse-error (type => 'unmatched end tag',
5298                                        text => $token->{tag_name},
5299                                        token => $token);
5300                      ## Ignore the token                      ## Ignore the token
5301                      !!!next-token;                      !!!next-token;
5302                      redo B;                      next B;
5303                    }                    }
5304                                    
5305                  ## generate implied end tags                  ## generate implied end tags
5306                  if ({                  while ($self->{open_elements}->[-1]->[1]
5307                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5308                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5309                       th => ($token->{tag_name} eq 'td'),                    pop @{$self->{open_elements}};
                      tr => 1,  
                      tbody => 1, tfoot=> 1, thead => 1,  
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5310                  }                  }
5311                    
5312                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5313                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5314                      !!!cp ('t167');
5315                      !!!parse-error (type => 'not closed',
5316                                      text => $self->{open_elements}->[-1]->[0]
5317                                          ->manakai_local_name,
5318                                      token => $token);
5319                    } else {
5320                      !!!cp ('t168');
5321                  }                  }
5322                                    
5323                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3430  sub _tree_construction_main ($) { Line 5327  sub _tree_construction_main ($) {
5327                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5328                                    
5329                  !!!next-token;                  !!!next-token;
5330                  redo B;                  next B;
5331                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5332                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5333                    !!!parse-error (type => 'unmatched end tag',
5334                                    text => $token->{tag_name}, token => $token);
5335                  ## Ignore the token                  ## Ignore the token
5336                  !!!next-token;                  !!!next-token;
5337                  redo B;                  next B;
5338                } else {                } else {
5339                    !!!cp ('t170');
5340                  #                  #
5341                }                }
5342              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5343                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5344                  ## have a table element in table scope                  ## have a table element in table scope
5345                  my $i;                  my $i;
5346                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5347                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5348                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5349                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5350                      last INSCOPE;                        !!!cp ('t171');
5351                    } elsif ({                        $i = $_;
5352                              table => 1, html => 1,                        last INSCOPE;
5353                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5354                      last INSCOPE;                        !!!cp ('t172');
5355                          last;
5356                        }
5357                    }                    }
5358    
5359                      !!!cp ('t173');
5360                      !!!parse-error (type => 'unmatched end tag',
5361                                      text => $token->{tag_name}, token => $token);
5362                      ## Ignore the token
5363                      !!!next-token;
5364                      next B;
5365                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5366                                    
5367                  ## generate implied end tags                  ## generate implied end tags
5368                  if ({                  while ($self->{open_elements}->[-1]->[1]
5369                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5370                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5371                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5372                  }                  }
5373                                    
5374                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5375                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5376                      !!!parse-error (type => 'not closed',
5377                                      text => $self->{open_elements}->[-1]->[0]
5378                                          ->manakai_local_name,
5379                                      token => $token);
5380                    } else {
5381                      !!!cp ('t176');
5382                  }                  }
5383                                    
5384                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3484  sub _tree_construction_main ($) { Line 5388  sub _tree_construction_main ($) {
5388                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5389                                    
5390                  !!!next-token;                  !!!next-token;
5391                  redo B;                  next B;
5392                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5393                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5394                    !!!parse-error (type => 'unmatched end tag',
5395                                    text => $token->{tag_name}, token => $token);
5396                  ## Ignore the token                  ## Ignore the token
5397                  !!!next-token;                  !!!next-token;
5398                  redo B;                  next B;
5399                } else {                } else {
5400                    !!!cp ('t178');
5401                  #                  #
5402                }                }
5403              } elsif ({              } elsif ({
# Line 3501  sub _tree_construction_main ($) { Line 5408  sub _tree_construction_main ($) {
5408                ## have an element in table scope                ## have an element in table scope
5409                my $i;                my $i;
5410                my $tn;                my $tn;
5411                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5412                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5413                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5414                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5415                    last INSCOPE;                      !!!cp ('t179');
5416                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5417                    $tn = $node->[1];  
5418                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5419                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5420                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5421                            table => 1, html => 1,                                line => $token->{line},
5422                           }->{$node->[1]}) {                                column => $token->{column}};
5423                    last INSCOPE;                      next B;
5424                      } elsif ($node->[1] & TABLE_CELL_EL) {
5425                        !!!cp ('t180');
5426                        $tn = $node->[0]->manakai_local_name;
5427                        ## NOTE: There is exactly one |td| or |th| element
5428                        ## in scope in the stack of open elements by definition.
5429                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5430                        ## ISSUE: Can this be reached?
5431                        !!!cp ('t181');
5432                        last;
5433                      }
5434                  }                  }
5435                } # INSCOPE  
5436                unless (defined $i) {                  !!!cp ('t182');
5437                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5438                        text => $token->{tag_name}, token => $token);
5439                  ## Ignore the token                  ## Ignore the token
5440                  !!!next-token;                  !!!next-token;
5441                  redo B;                  next B;
5442                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5443              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5444                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5445                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5446                                  token => $token);
5447    
5448                ## As if </caption>                ## As if </caption>
5449                ## have a table element in table scope                ## have a table element in table scope
5450                my $i;                my $i;
5451                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5452                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5453                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5454                      !!!cp ('t184');
5455                    $i = $_;                    $i = $_;
5456                    last INSCOPE;                    last INSCOPE;
5457                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5458                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5459                    last INSCOPE;                    last INSCOPE;
5460                  }                  }
5461                } # INSCOPE                } # INSCOPE
5462                unless (defined $i) {                unless (defined $i) {
5463                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5464                    !!!parse-error (type => 'unmatched end tag',
5465                                    text => 'caption', token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                }                }
5470                                
5471                ## generate implied end tags                ## generate implied end tags
5472                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5473                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5474                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5475                }                }
5476    
5477                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5478                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5479                    !!!parse-error (type => 'not closed',
5480                                    text => $self->{open_elements}->[-1]->[0]
5481                                        ->manakai_local_name,
5482                                    token => $token);
5483                  } else {
5484                    !!!cp ('t189');
5485                }                }
5486    
5487                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3577  sub _tree_construction_main ($) { Line 5491  sub _tree_construction_main ($) {
5491                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5492    
5493                ## reprocess                ## reprocess
5494                redo B;                next B;
5495              } elsif ({              } elsif ({
5496                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5497                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5498                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5499                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
5500                    !!!parse-error (type => 'unmatched end tag',
5501                                    text => $token->{tag_name}, token => $token);
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  redo B;                  next B;
5505                } else {                } else {
5506                    !!!cp ('t191');
5507                  #                  #
5508                }                }
5509              } elsif ({              } elsif ({
# Line 3594  sub _tree_construction_main ($) { Line 5511  sub _tree_construction_main ($) {
5511                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5512                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5513                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5514                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5515                  !!!parse-error (type => 'unmatched end tag',
5516                                  text => $token->{tag_name}, token => $token);
5517                ## Ignore the token                ## Ignore the token
5518                !!!next-token;                !!!next-token;
5519                redo B;                next B;
5520              } else {              } else {
5521                  !!!cp ('t193');
5522                #                #
5523              }              }
5524          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5525            for my $entry (@{$self->{open_elements}}) {
5526              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5527                !!!cp ('t75');
5528                !!!parse-error (type => 'in body:#eof', token => $token);
5529                last;
5530              }
5531            }
5532    
5533            ## Stop parsing.
5534            last B;
5535        } else {        } else {
5536          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5537        }        }
# Line 3609  sub _tree_construction_main ($) { Line 5540  sub _tree_construction_main ($) {
5540        #        #
5541      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5542        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5543              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5544                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5545              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5546                                
5547                unless (length $token->{data}) {            unless (length $token->{data}) {
5548                  !!!next-token;              !!!cp ('t194');
5549                  redo B;              !!!next-token;
5550                }              next B;
5551              }            } else {
5552                !!!cp ('t195');
5553              }
5554            }
5555    
5556              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5557    
5558              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5559              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 3626  sub _tree_construction_main ($) { Line 5561  sub _tree_construction_main ($) {
5561              ## result in a new Text node.              ## result in a new Text node.
5562              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5563                            
5564              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5565                # MUST                # MUST
5566                my $foster_parent_element;                my $foster_parent_element;
5567                my $next_sibling;                my $next_sibling;
5568                my $prev_sibling;                my $prev_sibling;
5569                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5570                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5571                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5572                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5573                        !!!cp ('t196');
5574                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5575                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5576                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5577                    } else {                    } else {
5578                        !!!cp ('t197');
5579                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5580                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5581                    }                    }
# Line 3653  sub _tree_construction_main ($) { Line 5587  sub _tree_construction_main ($) {
5587                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5588                if (defined $prev_sibling and                if (defined $prev_sibling and
5589                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5590                    !!!cp ('t198');
5591                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5592                } else {                } else {
5593                    !!!cp ('t199');
5594                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5595                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5596                     $next_sibling);                     $next_sibling);
5597                }                }
5598              } else {            $open_tables->[-1]->[1] = 1; # tainted
5599                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5600              }            !!!cp ('t200');
5601              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5602            }
5603                            
5604              !!!next-token;          !!!next-token;
5605              redo B;          next B;
5606        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5607              if ({          if ({
5608                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5609                   th => 1, td => 1,               th => 1, td => 1,
5610                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5611                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5612                  ## Clear back to table context              ## Clear back to table context
5613                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5614                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5615                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5616                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5617                  }              }
5618                                
5619                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5620                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5621                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5622                }            }
5623              
5624                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5625                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5626                    !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t202');
5627                  }                !!!parse-error (type => 'missing start tag:tr', token => $token);
5628                }
5629                                    
5630                  ## Clear back to table body context              ## Clear back to table body context
5631                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5632                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5633                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5634                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## ISSUE: Can this case be reached?
5635                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5636                  }              }
5637                                    
5638                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5639                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5640                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!cp ('t204');
5641                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5642                      !!!nack ('t204');
5643                    !!!next-token;                    !!!next-token;
5644                    redo B;                    next B;
5645                  } else {                  } else {
5646                    !!!insert-element ('tr');                    !!!cp ('t205');
5647                      !!!insert-element ('tr',, $token);
5648                    ## reprocess in the "in row" insertion mode                    ## reprocess in the "in row" insertion mode
5649                  }                  }
5650                  } else {
5651                    !!!cp ('t206');
5652                }                }
5653    
5654                ## Clear back to table row context                ## Clear back to table row context
5655                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5656                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5657                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5658                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5659                }                }
5660                                
5661                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5662                $self->{insertion_mode} = IN_CELL_IM;                $self->{insertion_mode} = IN_CELL_IM;
5663    
5664                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
5665                                
5666                  !!!nack ('t207.1');
5667                !!!next-token;                !!!next-token;
5668                redo B;                next B;
5669              } elsif ({              } elsif ({
5670                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5671                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 3733  sub _tree_construction_main ($) { Line 5677  sub _tree_construction_main ($) {
5677                  my $i;                  my $i;
5678                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5679                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5680                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5681                        !!!cp ('t208');
5682                      $i = $_;                      $i = $_;
5683                      last INSCOPE;                      last INSCOPE;
5684                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5685                              table => 1, html => 1,                      !!!cp ('t209');
                            }->{$node->[1]}) {  
5686                      last INSCOPE;                      last INSCOPE;
5687                    }                    }
5688                  } # INSCOPE                  } # INSCOPE
5689                  unless (defined $i) {                  unless (defined $i) {
5690                    !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                    !!!cp ('t210');
5691    ## TODO: This type is wrong.
5692                      !!!parse-error (type => 'unmacthed end tag',
5693                                      text => $token->{tag_name}, token => $token);
5694                    ## Ignore the token                    ## Ignore the token
5695                      !!!nack ('t210.1');
5696                    !!!next-token;                    !!!next-token;
5697                    redo B;                    next B;
5698                  }                  }
5699                                    
5700                  ## Clear back to table row context                  ## Clear back to table row context
5701                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5702                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5703                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
5704                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
5705                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5706                  }                  }
5707                                    
5708                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
5709                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5710                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5711                      !!!cp ('t212');
5712                    ## reprocess                    ## reprocess
5713                    redo B;                    !!!ack-later;
5714                      next B;
5715                  } else {                  } else {
5716                      !!!cp ('t213');
5717                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
5718                  }                  }
5719                }                }
# Line 3772  sub _tree_construction_main ($) { Line 5723  sub _tree_construction_main ($) {
5723                  my $i;                  my $i;
5724                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5725                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5726                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
5727                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
5728                      $i = $_;                      $i = $_;
5729                      last INSCOPE;                      last INSCOPE;
5730                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5731                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
5732                      last INSCOPE;                      last INSCOPE;
5733                    }                    }
5734                  } # INSCOPE                  } # INSCOPE
5735                  unless (defined $i) {                  unless (defined $i) {
5736                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
5737    ## TODO: This erorr type is wrong.
5738                      !!!parse-error (type => 'unmatched end tag',
5739                                      text => $token->{tag_name}, token => $token);
5740                    ## Ignore the token                    ## Ignore the token
5741                      !!!nack ('t216.1');
5742                    !!!next-token;                    !!!next-token;
5743                    redo B;                    next B;
5744                  }                  }
5745    
5746                  ## Clear back to table body context                  ## Clear back to table body context
5747                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5748                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5749                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5750                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5751                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5752                  }                  }
5753                                    
# Line 3808  sub _tree_construction_main ($) { Line 5761  sub _tree_construction_main ($) {
5761                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5762                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5763                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
5764                  } else {
5765                    !!!cp ('t218');
5766                }                }
5767    
5768                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
5769                  ## Clear back to table context                  ## Clear back to table context
5770                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5771                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5772                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t219');
5773                      ## ISSUE: Can this state be reached?
5774                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5775                  }                  }
5776                                    
5777                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup',, $token);
5778                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5779                  ## reprocess                  ## reprocess
5780                  redo B;                  !!!ack-later;
5781                    next B;
5782                } elsif ({                } elsif ({
5783                          caption => 1,                          caption => 1,
5784                          colgroup => 1,                          colgroup => 1,
5785                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
5786                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
5787                  ## Clear back to table context                  ## Clear back to table context
5788                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5789                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5790                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t220');
5791                      ## ISSUE: Can this state be reached?
5792                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5793                  }                  }
5794                                    
5795                  push @$active_formatting_elements, ['#marker', '']                  push @$active_formatting_elements, ['#marker', '']
5796                      if $token->{tag_name} eq 'caption';                      if $token->{tag_name} eq 'caption';
5797                                    
5798                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5799                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
5800                                             caption => IN_CAPTION_IM,                                             caption => IN_CAPTION_IM,
5801                                             colgroup => IN_COLUMN_GROUP_IM,                                             colgroup => IN_COLUMN_GROUP_IM,
# Line 3846  sub _tree_construction_main ($) { Line 5804  sub _tree_construction_main ($) {
5804                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
5805                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
5806                  !!!next-token;                  !!!next-token;
5807                  redo B;                  !!!nack ('t220.1');
5808                    next B;
5809                } else {                } else {
5810                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
5811                }                }
5812              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5813                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5814                                  text => $self->{open_elements}->[-1]->[0]
5815                                      ->manakai_local_name,
5816                                  token => $token);
5817    
5818                ## As if </table>                ## As if </table>
5819                ## have a table element in table scope                ## have a table element in table scope
5820                my $i;                my $i;
5821                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5822                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5823                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5824                      !!!cp ('t221');
5825                    $i = $_;                    $i = $_;
5826                    last INSCOPE;                    last INSCOPE;
5827                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5828                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5829                    last INSCOPE;                    last INSCOPE;
5830                  }                  }
5831                } # INSCOPE                } # INSCOPE
5832                unless (defined $i) {                unless (defined $i) {
5833                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5834    ## TODO: The following is wrong, maybe.
5835                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5836                                    token => $token);
5837                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5838                    !!!nack ('t223.1');
5839                  !!!next-token;                  !!!next-token;
5840                  redo B;                  next B;
5841                }                }
5842                                
5843    ## TODO: Followings are removed from the latest spec.
5844                ## generate implied end tags                ## generate implied end tags
5845                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5846                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5847                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5848                }                }
5849    
5850                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5851                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5852                    ## NOTE: |<table><tr><table>|
5853                    !!!parse-error (type => 'not closed',
5854                                    text => $self->{open_elements}->[-1]->[0]
5855                                        ->manakai_local_name,
5856                                    token => $token);
5857                  } else {
5858                    !!!cp ('t226');
5859                }                }
5860    
5861                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5862                  pop @{$open_tables};
5863    
5864                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5865    
5866                ## reprocess            ## reprocess
5867                redo B;            !!!ack-later;
5868          } else {            next B;
5869            !!!parse-error (type => 'in table:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'style') {
5870              if (not $open_tables->[-1]->[1]) { # tainted
5871                !!!cp ('t227.8');
5872                ## NOTE: This is a "as if in head" code clone.
5873                $parse_rcdata->(CDATA_CONTENT_MODEL);
5874                next B;
5875              } else {
5876                !!!cp ('t227.7');
5877                #
5878              }
5879            } elsif ($token->{tag_name} eq 'script') {
5880              if (not $open_tables->[-1]->[1]) { # tainted
5881                !!!cp ('t227.6');
5882                ## NOTE: This is a "as if in head" code clone.
5883                $script_start_tag->();
5884                next B;
5885              } else {
5886                !!!cp ('t227.5');
5887                #
5888              }
5889            } elsif ($token->{tag_name} eq 'input') {
5890              if (not $open_tables->[-1]->[1]) { # tainted
5891                if ($token->{attributes}->{type}) { ## TODO: case
5892                  my $type = lc $token->{attributes}->{type}->{value};
5893                  if ($type eq 'hidden') {
5894                    !!!cp ('t227.3');
5895                    !!!parse-error (type => 'in table',
5896                                    text => $token->{tag_name}, token => $token);
5897    
5898            $insert = $insert_to_foster;                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5899    
5900                    ## TODO: form element pointer
5901    
5902                    pop @{$self->{open_elements}};
5903    
5904                    !!!next-token;
5905                    !!!ack ('t227.2.1');
5906                    next B;
5907                  } else {
5908                    !!!cp ('t227.2');
5909                    #
5910                  }
5911                } else {
5912                  !!!cp ('t227.1');
5913                  #
5914                }
5915              } else {
5916                !!!cp ('t227.4');
5917                #
5918              }
5919            } else {
5920              !!!cp ('t227');
5921            #            #
5922          }          }
5923    
5924            !!!parse-error (type => 'in table', text => $token->{tag_name},
5925                            token => $token);
5926    
5927            $insert = $insert_to_foster;
5928            #
5929        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5930              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
5931                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 3911  sub _tree_construction_main ($) { Line 5933  sub _tree_construction_main ($) {
5933                my $i;                my $i;
5934                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5935                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5936                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5937                      !!!cp ('t228');
5938                    $i = $_;                    $i = $_;
5939                    last INSCOPE;                    last INSCOPE;
5940                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5941                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5942                    last INSCOPE;                    last INSCOPE;
5943                  }                  }
5944                } # INSCOPE                } # INSCOPE
5945                unless (defined $i) {                unless (defined $i) {
5946                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
5947                    !!!parse-error (type => 'unmatched end tag',
5948                                    text => $token->{tag_name}, token => $token);
5949                  ## Ignore the token                  ## Ignore the token
5950                    !!!nack ('t230.1');
5951                  !!!next-token;                  !!!next-token;
5952                  redo B;                  next B;
5953                  } else {
5954                    !!!cp ('t232');
5955                }                }
5956    
5957                ## Clear back to table row context                ## Clear back to table row context
5958                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5959                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5960                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5961                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5962                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5963                }                }
5964    
5965                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5966                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5967                !!!next-token;                !!!next-token;
5968                redo B;                !!!nack ('t231.1');
5969                  next B;
5970              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5971                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5972                  ## As if </tr>                  ## As if </tr>
# Line 3946  sub _tree_construction_main ($) { Line 5974  sub _tree_construction_main ($) {
5974                  my $i;                  my $i;
5975                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5976                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5977                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5978                        !!!cp ('t233');
5979                      $i = $_;                      $i = $_;
5980                      last INSCOPE;                      last INSCOPE;
5981                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5982                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
5983                      last INSCOPE;                      last INSCOPE;
5984                    }                    }
5985                  } # INSCOPE                  } # INSCOPE
5986                  unless (defined $i) {                  unless (defined $i) {
5987                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
5988    ## TODO: The following is wrong.
5989                      !!!parse-error (type => 'unmatched end tag',
5990                                      text => $token->{type}, token => $token);
5991                    ## Ignore the token                    ## Ignore the token
5992                      !!!nack ('t236.1');
5993                    !!!next-token;                    !!!next-token;
5994                    redo B;                    next B;
5995                  }                  }
5996                                    
5997                  ## Clear back to table row context                  ## Clear back to table row context
5998                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5999                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6000                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
6001                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6002                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6003                  }                  }
6004                                    
# Line 3980  sub _tree_construction_main ($) { Line 6012  sub _tree_construction_main ($) {
6012                  my $i;                  my $i;
6013                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6014                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6015                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6016                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
6017                      $i = $_;                      $i = $_;
6018                      last INSCOPE;                      last INSCOPE;
6019                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6020                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
6021                      last INSCOPE;                      last INSCOPE;
6022                    }                    }
6023                  } # INSCOPE                  } # INSCOPE
6024                  unless (defined $i) {                  unless (defined $i) {
6025                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
6026                      !!!parse-error (type => 'unmatched end tag',
6027                                      text => $token->{tag_name}, token => $token);
6028                    ## Ignore the token                    ## Ignore the token
6029                      !!!nack ('t239.1');
6030                    !!!next-token;                    !!!next-token;
6031                    redo B;                    next B;
6032                  }                  }
6033                                    
6034                  ## Clear back to table body context                  ## Clear back to table body context
6035                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6036                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
6037                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6038                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6039                  }                  }
6040                                    
# Line 4018  sub _tree_construction_main ($) { Line 6050  sub _tree_construction_main ($) {
6050                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6051                }                }
6052    
6053                  ## NOTE: </table> in the "in table" insertion mode.
6054                  ## When you edit the code fragment below, please ensure that
6055                  ## the code for <table> in the "in table" insertion mode
6056                  ## is synced with it.
6057    
6058                ## have a table element in table scope                ## have a table element in table scope
6059                my $i;                my $i;
6060                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6061                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6062                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6063                      !!!cp ('t241');
6064                    $i = $_;                    $i = $_;
6065                    last INSCOPE;                    last INSCOPE;
6066                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6067                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6068                    last INSCOPE;                    last INSCOPE;
6069                  }                  }
6070                } # INSCOPE                } # INSCOPE
6071                unless (defined $i) {                unless (defined $i) {
6072                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6073                    !!!parse-error (type => 'unmatched end tag',
6074                                    text => $token->{tag_name}, token => $token);
6075                  ## Ignore the token                  ## Ignore the token
6076                    !!!nack ('t243.1');
6077                  !!!next-token;                  !!!next-token;
6078                  redo B;                  next B;
               }  
   
               ## 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_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
                 
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6079                }                }
6080                                    
6081                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6082                  pop @{$open_tables};
6083                                
6084                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6085                                
6086                !!!next-token;                !!!next-token;
6087                redo B;                next B;
6088              } elsif ({              } elsif ({
6089                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6090                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4069  sub _tree_construction_main ($) { Line 6094  sub _tree_construction_main ($) {
6094                  my $i;                  my $i;
6095                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6096                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6097                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6098                        !!!cp ('t247');
6099                      $i = $_;                      $i = $_;
6100                      last INSCOPE;                      last INSCOPE;
6101                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6102                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
6103                      last INSCOPE;                      last INSCOPE;
6104                    }                    }
6105                  } # INSCOPE                  } # INSCOPE
6106                    unless (defined $i) {                    unless (defined $i) {
6107                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
6108                        !!!parse-error (type => 'unmatched end tag',
6109                                        text => $token->{tag_name}, token => $token);
6110                      ## Ignore the token                      ## Ignore the token
6111                        !!!nack ('t249.1');
6112                      !!!next-token;                      !!!next-token;
6113                      redo B;                      next B;
6114                    }                    }
6115                                    
6116                  ## As if </tr>                  ## As if </tr>
# Line 4090  sub _tree_construction_main ($) { Line 6118  sub _tree_construction_main ($) {
6118                  my $i;                  my $i;
6119                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6120                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6121                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6122                        !!!cp ('t250');
6123                      $i = $_;                      $i = $_;
6124                      last INSCOPE;                      last INSCOPE;
6125                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6126                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
6127                      last INSCOPE;                      last INSCOPE;
6128                    }                    }
6129                  } # INSCOPE                  } # INSCOPE
6130                    unless (defined $i) {                    unless (defined $i) {
6131                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
6132                        !!!parse-error (type => 'unmatched end tag',
6133                                        text => 'tr', token => $token);
6134                      ## Ignore the token                      ## Ignore the token
6135                        !!!nack ('t252.1');
6136                      !!!next-token;                      !!!next-token;
6137                      redo B;                      next B;
6138                    }                    }
6139                                    
6140                  ## Clear back to table row context                  ## Clear back to table row context
6141                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6142                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6143                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
6144                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6145                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6146                  }                  }
6147                                    
# Line 4123  sub _tree_construction_main ($) { Line 6154  sub _tree_construction_main ($) {
6154                my $i;                my $i;
6155                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6156                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6157                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6158                      !!!cp ('t254');
6159                    $i = $_;                    $i = $_;
6160                    last INSCOPE;                    last INSCOPE;
6161                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6162                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6163                    last INSCOPE;                    last INSCOPE;
6164                  }                  }
6165                } # INSCOPE                } # INSCOPE
6166                unless (defined $i) {                unless (defined $i) {
6167                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
6168                    !!!parse-error (type => 'unmatched end tag',
6169                                    text => $token->{tag_name}, token => $token);
6170                  ## Ignore the token                  ## Ignore the token
6171                    !!!nack ('t256.1');
6172                  !!!next-token;                  !!!next-token;
6173                  redo B;                  next B;
6174                }                }
6175    
6176                ## Clear back to table body context                ## Clear back to table body context
6177                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6178                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6179                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6180                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6181                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6182                }                }
6183    
6184                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6185                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6186                  !!!nack ('t257.1');
6187                !!!next-token;                !!!next-token;
6188                redo B;                next B;
6189              } elsif ({              } elsif ({
6190                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6191                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6192                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6193                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6194                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6195                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6196                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6197                !!!next-token;                            text => $token->{tag_name}, token => $token);
6198                redo B;            ## Ignore the token
6199          } else {            !!!nack ('t258.1');
6200            !!!parse-error (type => 'in table:/'.$token->{tag_name});             !!!next-token;
6201              next B;
6202            } else {
6203              !!!cp ('t259');
6204              !!!parse-error (type => 'in table:/',
6205                              text => $token->{tag_name}, token => $token);
6206    
6207            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6208            #            #
6209          }          }
6210          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6211            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6212                    @{$self->{open_elements}} == 1) { # redundant, maybe
6213              !!!parse-error (type => 'in body:#eof', token => $token);
6214              !!!cp ('t259.1');
6215              #
6216            } else {
6217              !!!cp ('t259.2');
6218              #
6219            }
6220    
6221            ## Stop parsing
6222            last B;
6223        } else {        } else {
6224          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6225        }        }
6226      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6227            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6228              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6229                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6230                unless (length $token->{data}) {                unless (length $token->{data}) {
6231                    !!!cp ('t260');
6232                  !!!next-token;                  !!!next-token;
6233                  redo B;                  next B;
6234                }                }
6235              }              }
6236                            
6237                !!!cp ('t261');
6238              #              #
6239            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6240              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6241                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
6242                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6243                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6244                  !!!ack ('t262.1');
6245                !!!next-token;                !!!next-token;
6246                redo B;                next B;
6247              } else {              } else {
6248                  !!!cp ('t263');
6249                #                #
6250              }              }
6251            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6252              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6253                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6254                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
6255                    !!!parse-error (type => 'unmatched end tag',
6256                                    text => 'colgroup', token => $token);
6257                  ## Ignore the token                  ## Ignore the token
6258                  !!!next-token;                  !!!next-token;
6259                  redo B;                  next B;
6260                } else {                } else {
6261                    !!!cp ('t265');
6262                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6263                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6264                  !!!next-token;                  !!!next-token;
6265                  redo B;                              next B;            
6266                }                }
6267              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6268                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
6269                  !!!parse-error (type => 'unmatched end tag',
6270                                  text => 'col', token => $token);
6271                ## Ignore the token                ## Ignore the token
6272                !!!next-token;                !!!next-token;
6273                redo B;                next B;
6274              } else {              } else {
6275                  !!!cp ('t267');
6276                #                #
6277              }              }
6278            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6279              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6280            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6281              !!!cp ('t270.2');
6282              ## Stop parsing.
6283              last B;
6284            } else {
6285              ## NOTE: As if </colgroup>.
6286              !!!cp ('t270.1');
6287              pop @{$self->{open_elements}}; # colgroup
6288              $self->{insertion_mode} = IN_TABLE_IM;
6289              ## Reprocess.
6290              next B;
6291            }
6292          } else {
6293            die "$0: $token->{type}: Unknown token type";
6294          }
6295    
6296            ## As if </colgroup>            ## As if </colgroup>
6297            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6298              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
6299    ## TODO: Wrong error type?
6300                !!!parse-error (type => 'unmatched end tag',
6301                                text => 'colgroup', token => $token);
6302              ## Ignore the token              ## Ignore the token
6303                !!!nack ('t269.1');
6304              !!!next-token;              !!!next-token;
6305              redo B;              next B;
6306            } else {            } else {
6307                !!!cp ('t270');
6308              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6309              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6310                !!!ack-later;
6311              ## reprocess              ## reprocess
6312              redo B;              next B;
6313            }            }
6314      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6315        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6316            !!!cp ('t271');
6317          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6318          !!!next-token;          !!!next-token;
6319          redo B;          next B;
6320        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6321              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6322                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6323                  ## As if </option>              !!!cp ('t272');
6324                  pop @{$self->{open_elements}};              ## As if </option>
6325                }              pop @{$self->{open_elements}};
6326              } else {
6327                !!!cp ('t273');
6328              }
6329    
6330                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6331                !!!next-token;            !!!nack ('t273.1');
6332                redo B;            !!!next-token;
6333              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6334                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6335                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6336                  pop @{$self->{open_elements}};              !!!cp ('t274');
6337                }              ## As if </option>
6338                pop @{$self->{open_elements}};
6339              } else {
6340                !!!cp ('t275');
6341              }
6342    
6343                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6344                  ## As if </optgroup>              !!!cp ('t276');
6345                  pop @{$self->{open_elements}};              ## As if </optgroup>
6346                }              pop @{$self->{open_elements}};
6347              } else {
6348                !!!cp ('t277');
6349              }
6350    
6351                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6352                !!!next-token;            !!!nack ('t277.1');
6353                redo B;            !!!next-token;
6354              } elsif ($token->{tag_name} eq 'select') {            next B;
6355                !!!parse-error (type => 'not closed:select');          } elsif ({
6356                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6357                ## have an element in table scope                   }->{$token->{tag_name}} or
6358                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6359                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6360                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6361                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6362                    $i = $_;                     tr => 1, td => 1, th => 1,
6363                    last INSCOPE;                    }->{$token->{tag_name}})) {
6364                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6365                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6366                           }->{$node->[1]}) {                            token => $token);
6367                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6368                  }            ## as if there were </select> (otherwise).
6369                } # INSCOPE            ## have an element in table scope
6370                unless (defined $i) {            my $i;
6371                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6372                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6373                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6374                  redo B;                !!!cp ('t278');
6375                }                $i = $_;
6376                  last INSCOPE;
6377                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6378                  !!!cp ('t279');
6379                  last INSCOPE;
6380                }
6381              } # INSCOPE
6382              unless (defined $i) {
6383                !!!cp ('t280');
6384                !!!parse-error (type => 'unmatched end tag',
6385                                text => 'select', token => $token);
6386                ## Ignore the token
6387                !!!nack ('t280.1');
6388                !!!next-token;
6389                next B;
6390              }
6391                                
6392                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6393              splice @{$self->{open_elements}}, $i;
6394    
6395                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6396    
6397                !!!next-token;            if ($token->{tag_name} eq 'select') {
6398                redo B;              !!!nack ('t281.2');
6399                !!!next-token;
6400                next B;
6401              } else {
6402                !!!cp ('t281.1');
6403                !!!ack-later;
6404                ## Reprocess the token.
6405                next B;
6406              }
6407          } else {          } else {
6408            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!cp ('t282');
6409              !!!parse-error (type => 'in select',
6410                              text => $token->{tag_name}, token => $token);
6411            ## Ignore the token            ## Ignore the token
6412              !!!nack ('t282.1');
6413            !!!next-token;            !!!next-token;
6414            redo B;            next B;
6415          }          }
6416        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6417              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6418                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6419                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6420                  ## As if </option>              !!!cp ('t283');
6421                  splice @{$self->{open_elements}}, -2;              ## As if </option>
6422                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              splice @{$self->{open_elements}}, -2;
6423                  pop @{$self->{open_elements}};            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6424                } else {              !!!cp ('t284');
6425                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              pop @{$self->{open_elements}};
6426                  ## Ignore the token            } else {
6427                }              !!!cp ('t285');
6428                !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6429                redo B;                              text => $token->{tag_name}, token => $token);
6430              } elsif ($token->{tag_name} eq 'option') {              ## Ignore the token
6431                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6432                  pop @{$self->{open_elements}};            !!!nack ('t285.1');
6433                } else {            !!!next-token;
6434                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            next B;
6435                  ## Ignore the token          } elsif ($token->{tag_name} eq 'option') {
6436                }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6437                !!!next-token;              !!!cp ('t286');
6438                redo B;              pop @{$self->{open_elements}};
6439              } elsif ($token->{tag_name} eq 'select') {            } else {
6440                ## have an element in table scope              !!!cp ('t287');
6441                my $i;              !!!parse-error (type => 'unmatched end tag',
6442                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                              text => $token->{tag_name}, token => $token);
6443                  my $node = $self->{open_elements}->[$_];              ## Ignore the token
6444                  if ($node->[1] eq $token->{tag_name}) {            }
6445                    $i = $_;            !!!nack ('t287.1');
6446                    last INSCOPE;            !!!next-token;
6447                  } elsif ({            next B;
6448                            table => 1, html => 1,          } elsif ($token->{tag_name} eq 'select') {
6449                           }->{$node->[1]}) {            ## have an element in table scope
6450                    last INSCOPE;            my $i;
6451                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6452                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6453                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6454                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t288');
6455                  ## Ignore the token                $i = $_;
6456                  !!!next-token;                last INSCOPE;
6457                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6458                }                !!!cp ('t289');
6459                  last INSCOPE;
6460                }
6461              } # INSCOPE
6462              unless (defined $i) {
6463                !!!cp ('t290');
6464                !!!parse-error (type => 'unmatched end tag',
6465                                text => $token->{tag_name}, token => $token);
6466                ## Ignore the token
6467                !!!nack ('t290.1');
6468                !!!next-token;
6469                next B;
6470              }
6471                                
6472                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6473              splice @{$self->{open_elements}}, $i;
6474    
6475                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6476    
6477                !!!next-token;            !!!nack ('t291.1');
6478                redo B;            !!!next-token;
6479              } elsif ({            next B;
6480                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6481                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6482                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6483                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6484                     }->{$token->{tag_name}}) {
6485    ## TODO: The following is wrong?
6486              !!!parse-error (type => 'unmatched end tag',
6487                              text => $token->{tag_name}, token => $token);
6488                                
6489                ## have an element in table scope            ## have an element in table scope
6490                my $i;            my $i;
6491                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6492                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6493                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6494                    $i = $_;                !!!cp ('t292');
6495                    last INSCOPE;                $i = $_;
6496                  } elsif ({                last INSCOPE;
6497                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6498                           }->{$node->[1]}) {                !!!cp ('t293');
6499                    last INSCOPE;                last INSCOPE;
6500                  }              }
6501                } # INSCOPE            } # INSCOPE
6502                unless (defined $i) {            unless (defined $i) {
6503                  ## Ignore the token              !!!cp ('t294');
6504                  !!!next-token;              ## Ignore the token
6505                  redo B;              !!!nack ('t294.1');
6506                }              !!!next-token;
6507                next B;
6508              }
6509                                
6510                ## As if </select>            ## As if </select>
6511                ## have an element in table scope            ## have an element in table scope
6512                undef $i;            undef $i;
6513                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6514                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6515                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6516                    $i = $_;                !!!cp ('t295');
6517                    last INSCOPE;                $i = $_;
6518                  } elsif ({                last INSCOPE;
6519                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6520                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
6521                    last INSCOPE;                !!!cp ('t296');
6522                  }                last INSCOPE;
6523                } # INSCOPE              }
6524                unless (defined $i) {            } # INSCOPE
6525                  !!!parse-error (type => 'unmatched end tag:select');            unless (defined $i) {
6526                  ## Ignore the </select> token              !!!cp ('t297');
6527                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
6528                  redo B;              !!!parse-error (type => 'unmatched end tag',
6529                }                              text => 'select', token => $token);
6530                ## Ignore the </select> token
6531                !!!nack ('t297.1');
6532                !!!next-token; ## TODO: ok?
6533                next B;
6534              }
6535                                
6536                splice @{$self->{open_elements}}, $i;            !!!cp ('t298');
6537              splice @{$self->{open_elements}}, $i;
6538    
6539                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6540    
6541                ## reprocess            !!!ack-later;
6542                redo B;            ## reprocess
6543              next B;
6544          } else {          } else {
6545            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!cp ('t299');
6546              !!!parse-error (type => 'in select:/',
6547                              text => $token->{tag_name}, token => $token);
6548            ## Ignore the token            ## Ignore the token
6549              !!!nack ('t299.3');
6550            !!!next-token;            !!!next-token;
6551            redo B;            next B;
6552            }
6553          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6554            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6555                    @{$self->{open_elements}} == 1) { # redundant, maybe
6556              !!!cp ('t299.1');
6557              !!!parse-error (type => 'in body:#eof', token => $token);
6558            } else {
6559              !!!cp ('t299.2');
6560          }          }
6561    
6562            ## Stop parsing.
6563            last B;
6564        } else {        } else {
6565          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6566        }        }
6567      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6568        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6569          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6570            my $data = $1;            my $data = $1;
6571            ## As if in body            ## As if in body
6572            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 4412  sub _tree_construction_main ($) { Line 6574  sub _tree_construction_main ($) {
6574            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6575                        
6576            unless (length $token->{data}) {            unless (length $token->{data}) {
6577                !!!cp ('t300');
6578              !!!next-token;              !!!next-token;
6579              redo B;              next B;
6580            }            }
6581          }          }
6582                    
6583          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6584            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
6585              !!!parse-error (type => 'after html:#text', token => $token);
6586            ## Reprocess in the "main" phase, "after body" insertion mode...            #
6587            } else {
6588              !!!cp ('t302');
6589              ## "after body" insertion mode
6590              !!!parse-error (type => 'after body:#text', token => $token);
6591              #
6592          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6593    
6594          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6595          ## reprocess          ## reprocess
6596          redo B;          next B;
6597        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6598          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6599            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
6600                        !!!parse-error (type => 'after html',
6601            ## Reprocess in the "main" phase, "after body" insertion mode...                            text => $token->{tag_name}, token => $token);
6602              #
6603            } else {
6604              !!!cp ('t304');
6605              ## "after body" insertion mode
6606              !!!parse-error (type => 'after body',
6607                              text => $token->{tag_name}, token => $token);
6608              #
6609          }          }
6610    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6611          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6612            !!!ack-later;
6613          ## reprocess          ## reprocess
6614          redo B;          next B;
6615        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6616          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6617            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
6618              !!!parse-error (type => 'after html:/',
6619                              text => $token->{tag_name}, token => $token);
6620                        
6621            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6622            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess.
6623              next B;
6624            } else {
6625              !!!cp ('t306');
6626          }          }
6627    
6628          ## "after body" insertion mode          ## "after body" insertion mode
6629          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6630            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6631              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
6632                !!!parse-error (type => 'unmatched end tag',
6633                                text => 'html', token => $token);
6634              ## Ignore the token              ## Ignore the token
6635              !!!next-token;              !!!next-token;
6636              redo B;              next B;
6637            } else {            } else {
6638                !!!cp ('t308');
6639              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6640              !!!next-token;              !!!next-token;
6641              redo B;              next B;
6642            }            }
6643          } else {          } else {
6644            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
6645              !!!parse-error (type => 'after body:/',
6646                              text => $token->{tag_name}, token => $token);
6647    
6648            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6649            ## reprocess            ## reprocess
6650            redo B;            next B;
6651          }          }
6652          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6653            !!!cp ('t309.2');
6654            ## Stop parsing
6655            last B;
6656        } else {        } else {
6657          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6658        }        }
6659      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6660        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6661          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6662            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6663                        
6664            unless (length $token->{data}) {            unless (length $token->{data}) {
6665                !!!cp ('t310');
6666              !!!next-token;              !!!next-token;
6667              redo B;              next B;
6668            }            }
6669          }          }
6670                    
6671          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6672            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6673              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
6674                !!!parse-error (type => 'in frameset:#text', token => $token);
6675            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6676              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
6677            } else { # "after html frameset"              !!!parse-error (type => 'after frameset:#text', token => $token);
6678              !!!parse-error (type => 'after html:#character');            } else { # "after after frameset"
6679                !!!cp ('t313');
6680              $self->{insertion_mode} = AFTER_FRAMESET_IM;              !!!parse-error (type => 'after html:#text', token => $token);
             ## Reprocess in the "main" phase, "after frameset"...  
             !!!parse-error (type => 'after frameset:#character');  
6681            }            }
6682                        
6683            ## Ignore the token.            ## Ignore the token.
6684            if (length $token->{data}) {            if (length $token->{data}) {
6685                !!!cp ('t314');
6686              ## reprocess the rest of characters              ## reprocess the rest of characters
6687            } else {            } else {
6688                !!!cp ('t315');
6689              !!!next-token;              !!!next-token;
6690            }            }
6691            redo B;            next B;
6692          }          }
6693                    
6694          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6695        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6696          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6697              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6698            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6699              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6700              !!!nack ('t318.1');
6701            !!!next-token;            !!!next-token;
6702            redo B;            next B;
6703          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6704                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6705            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
6706              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6707            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6708              !!!ack ('t319.1');
6709            !!!next-token;            !!!next-token;
6710            redo B;            next B;
6711          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6712            ## NOTE: As if in body.            !!!cp ('t320');
6713            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6714            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6715              next B;
6716    
6717              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6718              ## has no parse error.
6719          } else {          } else {
6720            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
6722            } else {              !!!parse-error (type => 'in frameset',
6723              !!!parse-error (type => 'after frameset:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6724              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6725                !!!cp ('t322');
6726                !!!parse-error (type => 'after frameset',
6727                                text => $token->{tag_name}, token => $token);
6728              } else { # "after after frameset"
6729                !!!cp ('t322.2');
6730                !!!parse-error (type => 'after after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732            }            }
6733            ## Ignore the token            ## Ignore the token
6734              !!!nack ('t322.1');
6735            !!!next-token;            !!!next-token;
6736            redo B;            next B;
6737          }          }
6738        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:/'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6739          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6740              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6741            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6742                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6743              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6744                !!!parse-error (type => 'unmatched end tag',
6745                                text => $token->{tag_name}, token => $token);
6746              ## Ignore the token              ## Ignore the token
6747              !!!next-token;              !!!next-token;
6748            } else {            } else {
6749                !!!cp ('t326');
6750              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6751              !!!next-token;              !!!next-token;
6752            }            }
6753    
6754            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6755                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6756                !!!cp ('t327');
6757              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6758              } else {
6759                !!!cp ('t328');
6760            }            }
6761            redo B;            next B;
6762          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6763                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6764              !!!cp ('t329');
6765            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6766            !!!next-token;            !!!next-token;
6767            redo B;            next B;
6768          } else {          } else {
6769            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6770              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
6771            } else {              !!!parse-error (type => 'in frameset:/',
6772              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6773              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6774                !!!cp ('t330.1');
6775                !!!parse-error (type => 'after frameset:/',
6776                                text => $token->{tag_name}, token => $token);
6777              } else { # "after after html"
6778                !!!cp ('t331');
6779                !!!parse-error (type => 'after after frameset:/',
6780                                text => $token->{tag_name}, token => $token);
6781            }            }
6782            ## Ignore the token            ## Ignore the token
6783            !!!next-token;            !!!next-token;
6784            redo B;            next B;
6785            }
6786          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6787            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6788                    @{$self->{open_elements}} == 1) { # redundant, maybe
6789              !!!cp ('t331.1');
6790              !!!parse-error (type => 'in body:#eof', token => $token);
6791            } else {
6792              !!!cp ('t331.2');
6793          }          }
6794            
6795            ## Stop parsing
6796            last B;
6797        } else {        } else {
6798          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6799        }        }
   
       ## ISSUE: An issue in spec here  
6800      } else {      } else {
6801        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6802      }      }
# Line 4591  sub _tree_construction_main ($) { Line 6804  sub _tree_construction_main ($) {
6804      ## "in body" insertion mode      ## "in body" insertion mode
6805      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
6806        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6807            !!!cp ('t332');
6808          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6809          $script_start_tag->($insert);          $script_start_tag->();
6810          redo B;          next B;
6811        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6812            !!!cp ('t333');
6813          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6814          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6815          redo B;          next B;
6816        } elsif ({        } elsif ({
6817                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6818                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6819            !!!cp ('t334');
6820          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6821          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6822          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6823            !!!ack ('t334.1');
6824          !!!next-token;          !!!next-token;
6825          redo B;          next B;
6826        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6827          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6828          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6829          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6830    
6831          unless ($self->{confident}) {          unless ($self->{confident}) {
6832            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6833                !!!cp ('t335');
6834                ## NOTE: Whether the encoding is supported or not is handled
6835                ## in the {change_encoding} callback.
6836              $self->{change_encoding}              $self->{change_encoding}
6837                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6838                            
6839              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6840                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6841                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6842                                           ->{has_reference});                                           ->{has_reference});
6843            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6844              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6845                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6846                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6847                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6848                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6849                       /x) {
6850                  !!!cp ('t336');
6851                  ## NOTE: Whether the encoding is supported or not is handled
6852                  ## in the {change_encoding} callback.
6853                $self->{change_encoding}                $self->{change_encoding}
6854                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6855                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6856                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6857                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 4637  sub _tree_construction_main ($) { Line 6860  sub _tree_construction_main ($) {
6860            }            }
6861          } else {          } else {
6862            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
6863                !!!cp ('t337');
6864              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6865                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6866                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6867                                           ->{has_reference});                                           ->{has_reference});
6868            }            }
6869            if ($token->{attributes}->{content}) {            if ($token->{attributes}->{content}) {
6870                !!!cp ('t338');
6871              $meta_el->[0]->get_attribute_node_ns (undef, 'content')              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6872                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6873                                       $token->{attributes}->{content}                                       $token->{attributes}->{content}
# Line 4650  sub _tree_construction_main ($) { Line 6875  sub _tree_construction_main ($) {
6875            }            }
6876          }          }
6877    
6878            !!!ack ('t338.1');
6879          !!!next-token;          !!!next-token;
6880          redo B;          next B;
6881        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6882          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
6883          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6884          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6885            if (defined $self->{head_element}) {          next B;
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6886        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6887          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6888                                
6889          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6890              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6891              !!!cp ('t342');
6892            ## Ignore the token            ## Ignore the token
6893          } else {          } else {
6894            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
6895            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
6896              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6897                  !!!cp ('t343');
6898                $body_el->set_attribute_ns                $body_el->set_attribute_ns
6899                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
6900                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
6901              }              }
6902            }            }
6903          }          }
6904            !!!nack ('t343.1');
6905          !!!next-token;          !!!next-token;
6906          redo B;          next B;
6907        } elsif ({        } elsif ({
6908                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6909                  div => 1, dl => 1, fieldset => 1, listing => 1,  
6910                  menu => 1, ol => 1, p => 1, ul => 1,                  ## NOTE: The normal one
6911                  pre => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6912                    center => 1, datagrid => 1, details => 1, dialog => 1,
6913                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6914                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6915                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6916                    section => 1, ul => 1,
6917                    ## NOTE: As normal, but drops leading newline
6918                    pre => 1, listing => 1,
6919                    ## NOTE: As normal, but interacts with the form element pointer
6920                    form => 1,
6921                    
6922                    table => 1,
6923                    hr => 1,
6924                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6925            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6926              !!!cp ('t350');
6927              !!!parse-error (type => 'in form:form', token => $token);
6928              ## Ignore the token
6929              !!!nack ('t350.1');
6930              !!!next-token;
6931              next B;
6932            }
6933    
6934          ## has a p element in scope          ## has a p element in scope
6935          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6936            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6937              !!!back-token;              !!!cp ('t344');
6938              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <form>
6939              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6940            } elsif ({                        line => $token->{line}, column => $token->{column}};
6941                      table => 1, caption => 1, td => 1, th => 1,              next B;
6942                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
6943                     }->{$_->[1]}) {              !!!cp ('t345');
6944              last INSCOPE;              last INSCOPE;
6945            }            }
6946          } # INSCOPE          } # INSCOPE
6947                        
6948          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6949          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6950              !!!nack ('t346.1');
6951            !!!next-token;            !!!next-token;
6952            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6953              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
6954              unless (length $token->{data}) {              unless (length $token->{data}) {
6955                  !!!cp ('t346');
6956                !!!next-token;                !!!next-token;
6957                } else {
6958                  !!!cp ('t349');
6959              }              }
6960              } else {
6961                !!!cp ('t348');
6962            }            }
6963          } else {          } elsif ($token->{tag_name} eq 'form') {
6964              !!!cp ('t347.1');
6965              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6966    
6967              !!!nack ('t347.2');
6968            !!!next-token;            !!!next-token;
6969          }          } elsif ($token->{tag_name} eq 'table') {
6970          redo B;            !!!cp ('t382');
6971        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6972          if (defined $self->{form_element}) {            
6973            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
6974            ## Ignore the token  
6975              !!!nack ('t382.1');
6976              !!!next-token;
6977            } elsif ($token->{tag_name} eq 'hr') {
6978              !!!cp ('t386');
6979              pop @{$self->{open_elements}};
6980            
6981              !!!nack ('t386.1');
6982            !!!next-token;            !!!next-token;
           redo B;  
6983          } else {          } else {
6984            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
               redo B;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
6985            !!!next-token;            !!!next-token;
           redo B;  
6986          }          }
6987            next B;
6988        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
6989          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
6990          INSCOPE: for (reverse @{$self->{open_elements}}) {  
6991            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6992              !!!back-token;            ## Interpreted as <li><foo/></li><li/> (non-conforming)
6993              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6994              redo B;            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6995            } elsif ({            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6996                      table => 1, caption => 1, td => 1, th => 1,            ## object (Fx)
6997                      button => 1, marquee => 1, object => 1, html => 1,            ## Generate non-tree (non-conforming)
6998                     }->{$_->[1]}) {            ## basefont (IE7 (where basefont is non-void)), center (IE),
6999              last INSCOPE;            ## form (IE), hn (IE)
7000            }          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7001          } # INSCOPE            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7002                        ## div (Fx, S)
7003          ## Step 1  
7004            my $non_optional;
7005          my $i = -1;          my $i = -1;
7006          my $node = $self->{open_elements}->[$i];  
7007          LI: {          ## 1.
7008            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7009            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7010              if ($i != -1) {              ## 2. (a) As if </li>
7011                !!!parse-error (type => 'end tag missing:'.              {
7012                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7013                  #
7014    
7015                  ## Otherwise
7016    
7017                  ## 1. generate implied end tags, except for </li>
7018                  #
7019    
7020                  ## 2. If current node != "li", parse error
7021                  if ($non_optional) {
7022                    !!!parse-error (type => 'not closed',
7023                                    text => $non_optional->[0]->manakai_local_name,
7024                                    token => $token);
7025                    !!!cp ('t355');
7026                  } else {
7027                    !!!cp ('t356');
7028                  }
7029    
7030                  ## 3. Pop
7031                  splice @{$self->{open_elements}}, $i;
7032              }              }
7033              splice @{$self->{open_elements}}, $i;  
7034              last LI;              last; ## 2. (b) goto 5.
7035            }            } elsif (
7036                                 ## NOTE: not "formatting" and not "phrasing"
7037            ## Step 3                     ($node->[1] & SPECIAL_EL or
7038            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7039                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7040                ($special_category->{$node->[1]} or  
7041                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7042                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7043              last LI;                     (not $node->[1] & P_EL)) {
7044                ## 3.
7045                !!!cp ('t357');
7046                last; ## goto 5.
7047              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7048                !!!cp ('t358');
7049                #
7050              } else {
7051                !!!cp ('t359');
7052                $non_optional ||= $node;
7053                #
7054            }            }
7055                        ## 4.
7056            ## Step 4            ## goto 2.
7057            $i--;            $i--;
7058            $node = $self->{open_elements}->[$i];          }
7059            redo LI;  
7060          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
7061          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7062            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7063              !!!back-token;              !!!cp ('t353');
7064              $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
7065              redo B;              ## NOTE: |<p><li>|, for example.
7066            } elsif ({  
7067                      table => 1, caption => 1, td => 1, th => 1,              !!!back-token; # <x>
7068                      button => 1, marquee => 1, object => 1, html => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7069                     }->{$_->[1]}) {                        line => $token->{line}, column => $token->{column}};
7070                next B;
7071              } elsif ($_->[1] & SCOPING_EL) {
7072                !!!cp ('t354');
7073              last INSCOPE;              last INSCOPE;
7074            }            }
7075          } # INSCOPE          } # INSCOPE
7076              
7077          ## Step 1          ## 5. (b) insert
7078            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7079            !!!nack ('t359.1');
7080            !!!next-token;
7081            next B;
7082          } elsif ($token->{tag_name} eq 'dt' or
7083                   $token->{tag_name} eq 'dd') {
7084            ## NOTE: As normal, but imply </dt> or </dd> when ...
7085    
7086            my $non_optional;
7087          my $i = -1;          my $i = -1;
7088          my $node = $self->{open_elements}->[$i];  
7089          LI: {          ## 1.
7090            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7091            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7092              if ($i != -1) {              ## 2. (a) As if </li>
7093                !!!parse-error (type => 'end tag missing:'.              {
7094                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7095                  #
7096    
7097                  ## Otherwise
7098    
7099                  ## 1. generate implied end tags, except for </dt> or </dd>
7100                  #
7101    
7102                  ## 2. If current node != "dt"|"dd", parse error
7103                  if ($non_optional) {
7104                    !!!parse-error (type => 'not closed',
7105                                    text => $non_optional->[0]->manakai_local_name,
7106                                    token => $token);
7107                    !!!cp ('t355.1');
7108                  } else {
7109                    !!!cp ('t356.1');
7110                  }
7111    
7112                  ## 3. Pop
7113                  splice @{$self->{open_elements}}, $i;
7114              }              }
7115              splice @{$self->{open_elements}}, $i;  
7116              last LI;              last; ## 2. (b) goto 5.
7117            }            } elsif (
7118                                 ## NOTE: not "formatting" and not "phrasing"
7119            ## Step 3                     ($node->[1] & SPECIAL_EL or
7120            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7121                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7122                ($special_category->{$node->[1]} or  
7123                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7124                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7125              last LI;                     (not $node->[1] & P_EL)) {
7126                ## 3.
7127                !!!cp ('t357.1');
7128                last; ## goto 5.
7129              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7130                !!!cp ('t358.1');
7131                #
7132              } else {
7133                !!!cp ('t359.1');
7134                $non_optional ||= $node;
7135                #
7136            }            }
7137                        ## 4.
7138            ## Step 4            ## goto 2.
7139            $i--;            $i--;
7140            $node = $self->{open_elements}->[$i];          }
7141            redo LI;  
7142          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
7143          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7144            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7145              !!!back-token;              !!!cp ('t353.1');
7146              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <x>
7147              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7148            } elsif ({                        line => $token->{line}, column => $token->{column}};
7149                      table => 1, caption => 1, td => 1, th => 1,              next B;
7150                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7151                     }->{$_->[1]}) {              !!!cp ('t354.1');
7152              last INSCOPE;              last INSCOPE;
7153            }            }
7154          } # INSCOPE          } # INSCOPE
7155              
7156          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          ## 5. (b) insert
7157                      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7158          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          !!!nack ('t359.2');
             
7159          !!!next-token;          !!!next-token;
7160          redo B;          next B;
7161        } elsif ({        } elsif ($token->{tag_name} eq 'plaintext') {
7162                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          ## NOTE: As normal, but effectively ends parsing
7163                 }->{$token->{tag_name}}) {  
7164          ## has a p element in scope          ## has a p element in scope
7165          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7166            my $node = $self->{open_elements}->[$_];            if ($_->[1] & P_EL) {
7167            if ($node->[1] eq 'p') {              !!!cp ('t367');
7168              !!!back-token;              !!!back-token; # <plaintext>
7169              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7170              redo B;                        line => $token->{line}, column => $token->{column}};
7171            } elsif ({              next B;
7172                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
7173                      button => 1, marquee => 1, object => 1, html => 1,              !!!cp ('t368');
                    }->{$node->[1]}) {  
7174              last INSCOPE;              last INSCOPE;
7175            }            }
7176          } # INSCOPE          } # INSCOPE
7177                        
7178          ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         ## 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;  
         #}  
7179                        
7180          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7181                        
7182            !!!nack ('t368.1');
7183          !!!next-token;          !!!next-token;
7184          redo B;          next B;
7185        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7186          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7187            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7188            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7189              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
7190                !!!parse-error (type => 'in a:a', token => $token);
7191                            
7192              !!!back-token;              !!!back-token; # <a>
7193              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7194              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7195                $formatting_end_tag->($token);
7196                            
7197              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7198                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7199                    !!!cp ('t372');
7200                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
7201                  last AFE2;                  last AFE2;
7202                }                }
7203              } # AFE2              } # AFE2
7204              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
7205                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7206                    !!!cp ('t373');
7207                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
7208                  last OE;                  last OE;
7209                }                }
7210              } # OE              } # OE
7211              last AFE;              last AFE;
7212            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
7213                !!!cp ('t374');
7214              last AFE;              last AFE;
7215            }            }
7216          } # AFE          } # AFE
7217                        
7218          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7219    
7220          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7221          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7222    
7223            !!!nack ('t374.1');
7224          !!!next-token;          !!!next-token;
7225          redo B;          next B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         redo B;  
7226        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7227          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7228    
7229          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7230          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7231            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7232            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7233              !!!parse-error (type => 'in nobr:nobr');              !!!cp ('t376');
7234              !!!back-token;              !!!parse-error (type => 'in nobr:nobr', token => $token);
7235              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              !!!back-token; # <nobr>
7236              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7237            } elsif ({                        line => $token->{line}, column => $token->{column}};
7238                      table => 1, caption => 1, td => 1, th => 1,              next B;
7239                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7240                     }->{$node->[1]}) {              !!!cp ('t377');
7241              last INSCOPE;              last INSCOPE;
7242            }            }
7243          } # INSCOPE          } # INSCOPE
7244                    
7245          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7246          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7247                    
7248            !!!nack ('t377.1');
7249          !!!next-token;          !!!next-token;
7250          redo B;          next B;
7251        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7252          ## has a button element in scope          ## has a button element in scope
7253          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7254            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7255            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7256              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
7257              !!!back-token;              !!!parse-error (type => 'in button:button', token => $token);
7258              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              !!!back-token; # <button>
7259              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7260            } elsif ({                        line => $token->{line}, column => $token->{column}};
7261                      table => 1, caption => 1, td => 1, th => 1,              next B;
7262                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7263                     }->{$node->[1]}) {              !!!cp ('t379');
7264              last INSCOPE;              last INSCOPE;
7265            }            }
7266          } # INSCOPE          } # INSCOPE
7267                        
7268          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7269                        
7270          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7271          push @$active_formatting_elements, ['#marker', ''];  
7272            ## TODO: associate with $self->{form_element} if defined
7273    
         !!!next-token;  
         redo B;  
       } 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});  
7274          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7275            
7276          !!!next-token;          !!!nack ('t379.1');
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
7277          !!!next-token;          !!!next-token;
7278          redo B;          next B;
7279        } elsif ({        } elsif ({
7280                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7281                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7282                  image => 1,                  noembed => 1,
7283                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7284                    noscript => 0, ## TODO: 1 if scripting is enabled
7285                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7286          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7287            !!!parse-error (type => 'image');            !!!cp ('t381');
7288            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
7289            } else {
7290              !!!cp ('t399');
7291          }          }
7292            ## NOTE: There is an "as if in body" code clone.
7293          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7294          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
7295        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7296          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7297                    
7298          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7299              !!!cp ('t389');
7300            ## Ignore the token            ## Ignore the token
7301              !!!nack ('t389'); ## NOTE: Not acknowledged.
7302            !!!next-token;            !!!next-token;
7303            redo B;            next B;
7304          } else {          } else {
7305              !!!ack ('t391.1');
7306    
7307            my $at = $token->{attributes};            my $at = $token->{attributes};
7308            my $form_attrs;            my $form_attrs;
7309            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5093  sub _tree_construction_main ($) { Line 7313  sub _tree_construction_main ($) {
7313            delete $at->{prompt};            delete $at->{prompt};
7314            my @tokens = (            my @tokens = (
7315                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7316                           attributes => $form_attrs},                           attributes => $form_attrs,
7317                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7318                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7319                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7320                            {type => START_TAG_TOKEN, tag_name => 'p',
7321                             line => $token->{line}, column => $token->{column}},
7322                            {type => START_TAG_TOKEN, tag_name => 'label',
7323                             line => $token->{line}, column => $token->{column}},
7324                         );                         );
7325            if ($prompt_attr) {            if ($prompt_attr) {
7326              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
7327                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7328                               #line => $token->{line}, column => $token->{column},
7329                              };
7330            } else {            } else {
7331                !!!cp ('t391');
7332              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7333                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7334                               #line => $token->{line}, column => $token->{column},
7335                              }; # SHOULD
7336              ## TODO: make this configurable              ## TODO: make this configurable
7337            }            }
7338            push @tokens,            push @tokens,
7339                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7340                             line => $token->{line}, column => $token->{column}},
7341                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7342                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7343                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7344                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7345                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7346            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7347                             line => $token->{line}, column => $token->{column}},
7348                            {type => END_TAG_TOKEN, tag_name => 'form',
7349                             line => $token->{line}, column => $token->{column}};
7350            !!!back-token (@tokens);            !!!back-token (@tokens);
7351            redo B;            !!!next-token;
7352              next B;
7353          }          }
7354        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7355          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7356          my $el;          my $el;
7357          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7358                    
7359          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7360          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5128  sub _tree_construction_main ($) { Line 7363  sub _tree_construction_main ($) {
7363          $insert->($el);          $insert->($el);
7364                    
7365          my $text = '';          my $text = '';
7366            !!!nack ('t392.1');
7367          !!!next-token;          !!!next-token;
7368          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7369            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
7370            unless (length $token->{data}) {            unless (length $token->{data}) {
7371                !!!cp ('t392');
7372              !!!next-token;              !!!next-token;
7373              } else {
7374                !!!cp ('t393');
7375            }            }
7376            } else {
7377              !!!cp ('t394');
7378          }          }
7379          while ($token->{type} == CHARACTER_TOKEN) {          while ($token->{type} == CHARACTER_TOKEN) {
7380              !!!cp ('t395');
7381            $text .= $token->{data};            $text .= $token->{data};
7382            !!!next-token;            !!!next-token;
7383          }          }
7384          if (length $text) {          if (length $text) {
7385              !!!cp ('t396');
7386            $el->manakai_append_text ($text);            $el->manakai_append_text ($text);
7387          }          }
7388                    
# Line 5147  sub _tree_construction_main ($) { Line 7390  sub _tree_construction_main ($) {
7390                    
7391          if ($token->{type} == END_TAG_TOKEN and          if ($token->{type} == END_TAG_TOKEN and
7392              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
7393              !!!cp ('t397');
7394            ## Ignore the token            ## Ignore the token
7395          } else {          } else {
7396            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!cp ('t398');
7397              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7398          }          }
7399          !!!next-token;          !!!next-token;
7400            next B;
7401          } elsif ($token->{tag_name} eq 'optgroup' or
7402                   $token->{tag_name} eq 'option') {
7403            ## has an |option| element in scope
7404            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7405              my $node = $self->{open_elements}->[$_];
7406              if ($node->[1] & OPTION_EL) {
7407                !!!cp ('t397.1');
7408                ## NOTE: As if </option>
7409                !!!back-token; # <option> or <optgroup>
7410                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7411                          line => $token->{line}, column => $token->{column}};
7412                next B;
7413              } elsif ($node->[1] & SCOPING_EL) {
7414                !!!cp ('t397.2');
7415                last INSCOPE;
7416              }
7417            } # INSCOPE
7418    
7419            $reconstruct_active_formatting_elements->($insert_to_current);
7420    
7421            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7422    
7423            !!!nack ('t397.3');
7424            !!!next-token;
7425          redo B;          redo B;
7426        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7427                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7428                  noembed => 1,          ## has a |ruby| element in scope
7429                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7430                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7431                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7432          ## NOTE: There is an "as if in body" code clone.              !!!cp ('t398.1');
7433          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              ## generate implied end tags
7434                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7435                  !!!cp ('t398.2');
7436                  pop @{$self->{open_elements}};
7437                }
7438                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7439                  !!!cp ('t398.3');
7440                  !!!parse-error (type => 'not closed',
7441                                  text => $self->{open_elements}->[-1]->[0]
7442                                      ->manakai_local_name,
7443                                  token => $token);
7444                  pop @{$self->{open_elements}}
7445                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7446                }
7447                last INSCOPE;
7448              } elsif ($node->[1] & SCOPING_EL) {
7449                !!!cp ('t398.4');
7450                last INSCOPE;
7451              }
7452            } # INSCOPE
7453    
7454            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7455    
7456            !!!nack ('t398.5');
7457            !!!next-token;
7458          redo B;          redo B;
7459        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7460                   $token->{tag_name} eq 'svg') {
7461          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7462    
7463            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7464    
7465            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7466    
7467            ## "adjust foreign attributes" - done in insert-element-f
7468                    
7469          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7470                    
7471          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7472              pop @{$self->{open_elements}};
7473              !!!ack ('t398.6');
7474            } else {
7475              !!!cp ('t398.7');
7476              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7477              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7478              ## mode, "in body" (not "in foreign content") secondary insertion
7479              ## mode, maybe.
7480            }
7481    
7482          !!!next-token;          !!!next-token;
7483          redo B;          next B;
7484        } elsif ({        } elsif ({
7485                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7486                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7487                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7488                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7489                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7490          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
7491            !!!parse-error (type => 'in body',
7492                            text => $token->{tag_name}, token => $token);
7493          ## Ignore the token          ## Ignore the token
7494            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7495            !!!next-token;
7496            next B;
7497          } elsif ($token->{tag_name} eq 'param' or
7498                   $token->{tag_name} eq 'source') {
7499            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7500            pop @{$self->{open_elements}};
7501    
7502            !!!ack ('t398.5');
7503          !!!next-token;          !!!next-token;
7504          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7505        } else {        } else {
7506            if ($token->{tag_name} eq 'image') {
7507              !!!cp ('t384');
7508              !!!parse-error (type => 'image', token => $token);
7509              $token->{tag_name} = 'img';
7510            } else {
7511              !!!cp ('t385');
7512            }
7513    
7514            ## NOTE: There is an "as if <br>" code clone.
7515          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7516                    
7517          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7518    
7519            if ({
7520                 applet => 1, marquee => 1, object => 1,
7521                }->{$token->{tag_name}}) {
7522              !!!cp ('t380');
7523              push @$active_formatting_elements, ['#marker', ''];
7524              !!!nack ('t380.1');
7525            } elsif ({
7526                      b => 1, big => 1, em => 1, font => 1, i => 1,
7527                      s => 1, small => 1, strike => 1,
7528                      strong => 1, tt => 1, u => 1,
7529                     }->{$token->{tag_name}}) {
7530              !!!cp ('t375');
7531              push @$active_formatting_elements, $self->{open_elements}->[-1];
7532              !!!nack ('t375.1');
7533            } elsif ($token->{tag_name} eq 'input') {
7534              !!!cp ('t388');
7535              ## TODO: associate with $self->{form_element} if defined
7536              pop @{$self->{open_elements}};
7537              !!!ack ('t388.2');
7538            } elsif ({
7539                      area => 1, basefont => 1, bgsound => 1, br => 1,
7540                      embed => 1, img => 1, spacer => 1, wbr => 1,
7541                     }->{$token->{tag_name}}) {
7542              !!!cp ('t388.1');
7543              pop @{$self->{open_elements}};
7544              !!!ack ('t388.3');
7545            } elsif ($token->{tag_name} eq 'select') {
7546              ## TODO: associate with $self->{form_element} if defined
7547            
7548              if ($self->{insertion_mode} & TABLE_IMS or
7549                  $self->{insertion_mode} & BODY_TABLE_IMS or
7550                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7551                !!!cp ('t400.1');
7552                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7553              } else {
7554                !!!cp ('t400.2');
7555                $self->{insertion_mode} = IN_SELECT_IM;
7556              }
7557              !!!nack ('t400.3');
7558            } else {
7559              !!!nack ('t402');
7560            }
7561                    
7562          !!!next-token;          !!!next-token;
7563          redo B;          next B;
7564        }        }
7565      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7566        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7567          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7568              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7569            for (@{$self->{open_elements}}) {          INSCOPE: {
7570              unless ({            for (reverse @{$self->{open_elements}}) {
7571                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7572                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7573                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7574                      }->{$_->[1]}) {                last INSCOPE;
7575                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
7576                  !!!cp ('t405.1');
7577                  last;
7578              }              }
7579            }            }
7580    
7581            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7582            !!!next-token;  
7583            redo B;            !!!parse-error (type => 'unmatched end tag',
7584          } else {                            text => $token->{tag_name}, token => $token);
7585            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: Ignore the token.
           ## Ignore the token  
7586            !!!next-token;            !!!next-token;
7587            redo B;            next B;
7588            } # INSCOPE
7589    
7590            for (@{$self->{open_elements}}) {
7591              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7592                !!!cp ('t403');
7593                !!!parse-error (type => 'not closed',
7594                                text => $_->[0]->manakai_local_name,
7595                                token => $token);
7596                last;
7597              } else {
7598                !!!cp ('t404');
7599              }
7600          }          }
7601    
7602            $self->{insertion_mode} = AFTER_BODY_IM;
7603            !!!next-token;
7604            next B;
7605        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7606          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
7607            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7608            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7609              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              $self->{open_elements}->[1]->[1] & BODY_EL) {
7610              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7611                !!!cp ('t406');
7612                !!!parse-error (type => 'not closed',
7613                                text => $self->{open_elements}->[1]->[0]
7614                                    ->manakai_local_name,
7615                                token => $token);
7616              } else {
7617                !!!cp ('t407');
7618            }            }
7619            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7620            ## reprocess            ## reprocess
7621            redo B;            next B;
7622          } else {          } else {
7623            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
7624              !!!parse-error (type => 'unmatched end tag',
7625                              text => $token->{tag_name}, token => $token);
7626            ## Ignore the token            ## Ignore the token
7627            !!!next-token;            !!!next-token;
7628            redo B;            next B;
7629          }          }
7630        } elsif ({        } elsif ({
7631                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7632                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7633                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7634                  p => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
7635                    center => 1, datagrid => 1, details => 1, dialog => 1,
7636                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7637                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7638                    ol => 1, pre => 1, section => 1, ul => 1,
7639    
7640                    ## NOTE: As normal, but ... optional tags
7641                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7642                  button => 1, marquee => 1, object => 1,  
7643                    applet => 1, button => 1, marquee => 1, object => 1,
7644                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7645            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7646            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7647            ## </dd>" code.
7648    
7649          ## has an element in scope          ## has an element in scope
7650          my $i;          my $i;
7651          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7652            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7653            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7654              ## generate implied end tags              !!!cp ('t410');
             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_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7655              $i = $_;              $i = $_;
7656              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7657            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7658                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7659              last INSCOPE;              last INSCOPE;
7660            }            }
7661          } # INSCOPE          } # INSCOPE
7662            
7663          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7664            if (defined $i) {            !!!cp ('t413');
7665              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag',
7666                              text => $token->{tag_name}, token => $token);
7667              ## NOTE: Ignore the token.
7668            } else {
7669              ## Step 1. generate implied end tags
7670              while ({
7671                      ## END_TAG_OPTIONAL_EL
7672                      dd => ($token->{tag_name} ne 'dd'),
7673                      dt => ($token->{tag_name} ne 'dt'),
7674                      li => ($token->{tag_name} ne 'li'),
7675                      option => 1,
7676                      optgroup => 1,
7677                      p => 1,
7678                      rt => 1,
7679                      rp => 1,
7680                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7681                !!!cp ('t409');
7682                pop @{$self->{open_elements}};
7683              }
7684    
7685              ## Step 2.
7686              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7687                      ne $token->{tag_name}) {
7688                !!!cp ('t412');
7689                !!!parse-error (type => 'not closed',
7690                                text => $self->{open_elements}->[-1]->[0]
7691                                    ->manakai_local_name,
7692                                token => $token);
7693            } else {            } else {
7694              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
7695            }            }
7696          }  
7697                      ## Step 3.
         if (defined $i) {  
7698            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7699          } elsif ($token->{tag_name} eq 'p') {  
7700            ## As if <p>, then reprocess the current token            ## Step 4.
7701            my $el;            $clear_up_to_marker->()
7702            !!!create-element ($el, 'p');                if {
7703            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
7704                  }->{$token->{tag_name}};
7705          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
7706          !!!next-token;          !!!next-token;
7707          redo B;          next B;
7708        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7709            ## NOTE: As normal, but interacts with the form element pointer
7710    
7711            undef $self->{form_element};
7712    
7713          ## has an element in scope          ## has an element in scope
7714            my $i;
7715          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7716            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7717            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7718              ## generate implied end tags              !!!cp ('t418');
7719              if ({              $i = $_;
                  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_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7720              last INSCOPE;              last INSCOPE;
7721            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7722                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7723              last INSCOPE;              last INSCOPE;
7724            }            }
7725          } # INSCOPE          } # INSCOPE
7726            
7727          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7728            pop @{$self->{open_elements}};            !!!cp ('t421');
7729          } else {            !!!parse-error (type => 'unmatched end tag',
7730            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
7731              ## NOTE: Ignore the token.
7732            } else {
7733              ## Step 1. generate implied end tags
7734              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7735                !!!cp ('t417');
7736                pop @{$self->{open_elements}};
7737              }
7738              
7739              ## Step 2.
7740              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7741                      ne $token->{tag_name}) {
7742                !!!cp ('t417.1');
7743                !!!parse-error (type => 'not closed',
7744                                text => $self->{open_elements}->[-1]->[0]
7745                                    ->manakai_local_name,
7746                                token => $token);
7747              } else {
7748                !!!cp ('t420');
7749              }  
7750              
7751              ## Step 3.
7752              splice @{$self->{open_elements}}, $i;
7753          }          }
7754    
         undef $self->{form_element};  
7755          !!!next-token;          !!!next-token;
7756          redo B;          next B;
7757        } elsif ({        } elsif ({
7758                    ## NOTE: As normal, except acts as a closer for any ...
7759                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7760                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7761          ## has an element in scope          ## has an element in scope
7762          my $i;          my $i;
7763          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7764            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7765            if ({            if ($node->[1] & HEADING_EL) {
7766                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,              !!!cp ('t423');
               }->{$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_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7767              $i = $_;              $i = $_;
7768              last INSCOPE;              last INSCOPE;
7769            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7770                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7771              last INSCOPE;              last INSCOPE;
7772            }            }
7773          } # INSCOPE          } # INSCOPE
7774            
7775          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7776            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t425.1');
7777              !!!parse-error (type => 'unmatched end tag',
7778                              text => $token->{tag_name}, token => $token);
7779              ## NOTE: Ignore the token.
7780            } else {
7781              ## Step 1. generate implied end tags
7782              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7783                !!!cp ('t422');
7784                pop @{$self->{open_elements}};
7785              }
7786              
7787              ## Step 2.
7788              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7789                      ne $token->{tag_name}) {
7790                !!!cp ('t425');
7791                !!!parse-error (type => 'unmatched end tag',
7792                                text => $token->{tag_name}, token => $token);
7793              } else {
7794                !!!cp ('t426');
7795              }
7796    
7797              ## Step 3.
7798              splice @{$self->{open_elements}}, $i;
7799          }          }
7800                    
         splice @{$self->{open_elements}}, $i if defined $i;  
7801          !!!next-token;          !!!next-token;
7802          redo B;          next B;
7803          } elsif ($token->{tag_name} eq 'p') {
7804            ## NOTE: As normal, except </p> implies <p> and ...
7805    
7806            ## has an element in scope
7807            my $non_optional;
7808            my $i;
7809            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7810              my $node = $self->{open_elements}->[$_];
7811              if ($node->[1] & P_EL) {
7812                !!!cp ('t410.1');
7813                $i = $_;
7814                last INSCOPE;
7815              } elsif ($node->[1] & SCOPING_EL) {
7816                !!!cp ('t411.1');
7817                last INSCOPE;
7818              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7819                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7820                !!!cp ('t411.2');
7821                #
7822              } else {
7823                !!!cp ('t411.3');
7824                $non_optional ||= $node;
7825                #
7826              }
7827            } # INSCOPE
7828    
7829            if (defined $i) {
7830              ## 1. Generate implied end tags
7831              #
7832    
7833              ## 2. If current node != "p", parse error
7834              if ($non_optional) {
7835                !!!cp ('t412.1');
7836                !!!parse-error (type => 'not closed',
7837                                text => $non_optional->[0]->manakai_local_name,
7838                                token => $token);
7839              } else {
7840                !!!cp ('t414.1');
7841              }
7842    
7843              ## 3. Pop
7844              splice @{$self->{open_elements}}, $i;
7845            } else {
7846              !!!cp ('t413.1');
7847              !!!parse-error (type => 'unmatched end tag',
7848                              text => $token->{tag_name}, token => $token);
7849    
7850              !!!cp ('t415.1');
7851              ## As if <p>, then reprocess the current token
7852              my $el;
7853              !!!create-element ($el, $HTML_NS, 'p',, $token);
7854              $insert->($el);
7855              ## NOTE: Not inserted into |$self->{open_elements}|.
7856            }
7857    
7858            !!!next-token;
7859            next B;
7860        } elsif ({        } elsif ({
7861                  a => 1,                  a => 1,
7862                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7863                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7864                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7865                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7866          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
7867          redo B;          $formatting_end_tag->($token);
7868            next B;
7869        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7870          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
7871            !!!parse-error (type => 'unmatched end tag',
7872                            text => 'br', token => $token);
7873    
7874          ## As if <br>          ## As if <br>
7875          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7876                    
7877          my $el;          my $el;
7878          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7879          $insert->($el);          $insert->($el);
7880                    
7881          ## Ignore the token.          ## Ignore the token.
7882          !!!next-token;          !!!next-token;
7883          redo B;          next B;
       } 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;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7884        } else {        } else {
7885            if ($token->{tag_name} eq 'sarcasm') {
7886              sleep 0.001; # take a deep breath
7887            }
7888    
7889          ## Step 1          ## Step 1
7890          my $node_i = -1;          my $node_i = -1;
7891          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7892    
7893          ## Step 2          ## Step 2
7894          S2: {          S2: {
7895            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7896              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7897              if ($node_tag_name eq $token->{tag_name}) {
7898              ## Step 1              ## Step 1
7899              ## generate implied end tags              ## generate implied end tags
7900              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7901                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
7902                   td => 1, th => 1, tr => 1,                ## NOTE: |<ruby><rt></ruby>|.
7903                   tbody => 1, tfoot => 1, thead => 1,                ## ISSUE: <ruby><rt></rt> will also take this code path,
7904                  }->{$self->{open_elements}->[-1]->[1]}) {                ## which seems wrong.
7905                !!!back-token;                pop @{$self->{open_elements}};
7906                $token = {type => END_TAG_TOKEN,                $node_i++;
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
7907              }              }
7908                    
7909              ## Step 2              ## Step 2
7910              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7911                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7912                $current_tag_name =~ tr/A-Z/a-z/;
7913                if ($current_tag_name ne $token->{tag_name}) {
7914                  !!!cp ('t431');
7915                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7916                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7917                                  text => $self->{open_elements}->[-1]->[0]
7918                                      ->manakai_local_name,
7919                                  token => $token);
7920                } else {
7921                  !!!cp ('t432');
7922              }              }
7923                            
7924              ## Step 3              ## Step 3
7925              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7926    
7927              !!!next-token;              !!!next-token;
7928              last S2;              last S2;
7929            } else {            } else {
7930              ## Step 3              ## Step 3
7931              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7932                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7933                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7934                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7935                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
7936                  !!!parse-error (type => 'unmatched end tag',
7937                                  text => $token->{tag_name}, token => $token);
7938                ## Ignore the token                ## Ignore the token
7939                !!!next-token;                !!!next-token;
7940                last S2;                last S2;
7941    
7942                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7943                  ## 9.27, "a" is a child of <dd> (conforming).  In
7944                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7945                  ## "a" is a child of both <body> and <dd>.
7946              }              }
7947                
7948                !!!cp ('t434');
7949            }            }
7950                        
7951            ## Step 4            ## Step 4
# Line 5451  sub _tree_construction_main ($) { Line 7955  sub _tree_construction_main ($) {
7955            ## Step 5;            ## Step 5;
7956            redo S2;            redo S2;
7957          } # S2          } # S2
7958          redo B;          next B;
7959        }        }
7960      }      }
7961      redo B;      next B;
7962      } continue { # B
7963        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7964          ## NOTE: The code below is executed in cases where it does not have
7965          ## to be, but it it is harmless even in those cases.
7966          ## has an element in scope
7967          INSCOPE: {
7968            for (reverse 0..$#{$self->{open_elements}}) {
7969              my $node = $self->{open_elements}->[$_];
7970              if ($node->[1] & FOREIGN_EL) {
7971                last INSCOPE;
7972              } elsif ($node->[1] & SCOPING_EL) {
7973                last;
7974              }
7975            }
7976            
7977            ## NOTE: No foreign element in scope.
7978            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7979          } # INSCOPE
7980        }
7981    } # B    } # B
7982    
   ## NOTE: The "trailing end" phase in HTML5 is split into  
   ## two insertion modes: "after html body" and "after html frameset".  
   ## NOTE: States in the main stage is preserved while  
   ## the parser stays in the trailing end phase. # MUST  
   
7983    ## Stop parsing # MUST    ## Stop parsing # MUST
7984        
7985    ## TODO: script stuffs    ## TODO: script stuffs
7986  } # _tree_construct_main  } # _tree_construct_main
7987    
7988  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7989    my $class = shift;    my $class = shift;
7990    my $node = shift;    my $node = shift;
7991    my $s = \$_[0];    #my $s = \$_[0];
7992    my $onerror = $_[1];    my $onerror = $_[1];
7993      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7994    
7995    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7996    
# Line 5490  sub set_inner_html ($$$) { Line 8009  sub set_inner_html ($$$) {
8009      }      }
8010    
8011      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8012      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8013    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8014      ## TODO: If non-html element      ## TODO: If non-html element
8015    
8016      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8017    
8018    ## TODO: Support for $get_wrapper
8019    
8020      ## Step 1 # MUST      ## Step 1 # MUST
8021      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8022      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5503  sub set_inner_html ($$$) { Line 8024  sub set_inner_html ($$$) {
8024      my $p = $class->new;      my $p = $class->new;
8025      $p->{document} = $doc;      $p->{document} = $doc;
8026    
8027      ## Step 9 # MUST      ## Step 8 # MUST
8028      my $i = 0;      my $i = 0;
8029      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8030      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8031      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8032        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8033        $input = $get_wrapper->($input);
8034        $p->{set_nc} = sub {
8035        my $self = shift;        my $self = shift;
8036    
8037        pop @{$self->{prev_input_character}};        my $char = '';
8038        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8039            $char = $self->{next_nc};
8040            delete $self->{next_nc};
8041            $self->{nc} = ord $char;
8042          } else {
8043            $self->{char_buffer} = '';
8044            $self->{char_buffer_pos} = 0;
8045            
8046            my $count = $input->manakai_read_until
8047                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8048                 $self->{char_buffer_pos});
8049            if ($count) {
8050              $self->{line_prev} = $self->{line};
8051              $self->{column_prev} = $self->{column};
8052              $self->{column}++;
8053              $self->{nc}
8054                  = ord substr ($self->{char_buffer},
8055                                $self->{char_buffer_pos}++, 1);
8056              return;
8057            }
8058            
8059            if ($input->read ($char, 1)) {
8060              $self->{nc} = ord $char;
8061            } else {
8062              $self->{nc} = -1;
8063              return;
8064            }
8065          }
8066    
8067        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8068        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
8069        $column++;  
8070          if ($self->{nc} == 0x000A) { # LF
8071        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
8072          $line++;          $p->{column} = 0;
8073          $column = 0;          !!!cp ('i1');
8074        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8075          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8076          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
8077          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8078          $column = 0;            $self->{next_nc} = $next;
8079        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
8080          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
8081        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
8082            $p->{column} = 0;
8083            !!!cp ('i2');
8084          } elsif ($self->{nc} == 0x0000) { # NULL
8085            !!!cp ('i4');
8086          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8087          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8088        }        }
8089      };      };
8090      $p->{prev_input_character} = [-1, -1, -1];  
8091      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8092              #my ($scalar, $specials_range, $offset) = @_;
8093          return 0 if defined $p->{next_nc};
8094    
8095          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8096          my $offset = $_[2] || 0;
8097          
8098          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8099            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8100            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8101              substr ($_[0], $offset)
8102                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8103              my $count = $+[0] - $-[0];
8104              if ($count) {
8105                $p->{column} += $count;
8106                $p->{char_buffer_pos} += $count;
8107                $p->{line_prev} = $p->{line};
8108                $p->{column_prev} = $p->{column} - 1;
8109                $p->{nc} = -1;
8110              }
8111              return $count;
8112            } else {
8113              return 0;
8114            }
8115          } else {
8116            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8117            if ($count) {
8118              $p->{column} += $count;
8119              $p->{column_prev} += $count;
8120              $p->{nc} = -1;
8121            }
8122            return $count;
8123          }
8124        }; # $p->{read_until}
8125    
8126      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8127        my (%opt) = @_;        my (%opt) = @_;
8128        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8129          my $column = $opt{column};
8130          if (defined $opt{token} and defined $opt{token}->{line}) {
8131            $line = $opt{token}->{line};
8132            $column = $opt{token}->{column};
8133          }
8134          warn "Parse error ($opt{type}) at line $line column $column\n";
8135      };      };
8136      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8137        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8138      };      };
8139            
8140        my $char_onerror = sub {
8141          my (undef, $type, %opt) = @_;
8142          $ponerror->(layer => 'encode',
8143                      line => $p->{line}, column => $p->{column} + 1,
8144                      %opt, type => $type);
8145        }; # $char_onerror
8146        $input->onerror ($char_onerror);
8147    
8148      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8149      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8150    
# Line 5564  sub set_inner_html ($$$) { Line 8166  sub set_inner_html ($$$) {
8166          unless defined $p->{content_model};          unless defined $p->{content_model};
8167          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8168    
8169      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8170          ## TODO: Foreign element OK?
8171    
8172      ## Step 4      ## Step 3
8173      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8174        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8175    
8176      ## Step 5 # MUST      ## Step 4 # MUST
8177      $doc->append_child ($root);      $doc->append_child ($root);
8178    
8179      ## Step 6 # MUST      ## Step 5 # MUST
8180      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8181    
8182      undef $p->{head_element};      undef $p->{head_element};
8183    
8184      ## Step 7 # MUST      ## Step 6 # MUST
8185      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8186    
8187      ## Step 8 # MUST      ## Step 7 # MUST
8188      my $anode = $node;      my $anode = $node;
8189      AN: while (defined $anode) {      AN: while (defined $anode) {
8190        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8191          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8192          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8193            if ($anode->manakai_local_name eq 'form') {            if ($anode->manakai_local_name eq 'form') {
8194                !!!cp ('i5');
8195              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8196              last AN;              last AN;
8197            }            }
# Line 5596  sub set_inner_html ($$$) { Line 8200  sub set_inner_html ($$$) {
8200        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8201      } # AN      } # AN
8202            
8203      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8204      {      {
8205        my $self = $p;        my $self = $p;
8206        !!!next-token;        !!!next-token;
8207      }      }
8208      $p->_tree_construction_main;      $p->_tree_construction_main;
8209    
8210      ## Step 11 # MUST      ## Step 10 # MUST
8211      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8212      for (@cn) {      for (@cn) {
8213        $node->remove_child ($_);        $node->remove_child ($_);
8214      }      }
8215      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8216    
8217      ## Step 12 # MUST      ## Step 11 # MUST
8218      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8219      for (@cn) {      for (@cn) {
8220        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5620  sub set_inner_html ($$$) { Line 8223  sub set_inner_html ($$$) {
8223      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8224    
8225      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8226    
8227        delete $p->{parse_error}; # delete loop
8228    } else {    } else {
8229      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8230    }    }

Legend:
Removed from v.1.72  
changed lines
  Added in v.1.201

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24