/[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.120 by wakaba, Thu Mar 20 03:57:00 2008 UTC revision 1.203 by wakaba, Sat Oct 4 17:16:02 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 = {  
   applet => 1, 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];
400    my $s;  
401        my $onerror = $_[2] || sub {
402    if (defined $charset) {      my (%opt) = @_;
403      require Encode; ## TODO: decode(utf8) don't delete BOM      warn "Parse error ($opt{type})\n";
404      $s = \ (Encode::decode ($charset, $$bytes_s));    };
405      $self->{input_encoding} = lc $charset; ## TODO: normalize name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406      $self->{confident} = 1;  
407    } else {    my $get_wrapper = $_[3] || sub ($) {
408      ## TODO: Implement HTML5 detection algorithm      return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411      ## 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
447        my $byte_buffer = '';
448        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;
461          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        ## Step 4
479        ## TODO: <meta charset>
480    
481        ## Step 5
482        ## TODO: from history
483    
484        ## Step 6
485      require Whatpm::Charset::UniversalCharDet;      require Whatpm::Charset::UniversalCharDet;
486      $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string      $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487          (substr ($$bytes_s, 0, 1024));          ($byte_buffer);
488      $charset ||= 'windows-1252';      if (defined $charset_name) {
489      $s = \ (Encode::decode ($charset, $$bytes_s));        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490      $self->{input_encoding} = $charset;  
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;      $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 {    $self->{change_encoding} = sub {
551      my $self = shift;      my $self = shift;
552      my $charset = lc shift;      $charset_name = shift;
553      my $token = shift;      my $token = shift;
     ## TODO: if $charset is supported  
     ## TODO: normalize charset name  
554    
555      ## "Change the encoding" algorithm:      $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            ## Step 1    
564      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?        if ($charset->{category} &
565        $charset = 'utf-8';            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      ## Step 2        !!!parse-error (type => 'charset label detected',
584      if (defined $self->{input_encoding} and                        text => $self->{input_encoding},
585          $self->{input_encoding} eq $charset) {                        value => $charset_name,
586        $self->{confident} = 1;                        level => $self->{level}->{warn},
587        return;                        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}
600    
601      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.    my $char_onerror = sub {
602          ':'.$charset, level => 'w', token => $token);      my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604      ## Step 3                      line => $self->{line}, column => $self->{column} + 1,
605      # if (can) {                      %opt, type => $type);
606        ## change the encoding on the fly.      if ($opt{octets}) {
607        #$self->{confident} = 1;        ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        #return;      }
609      # }    };
610    
611      ## Step 4    my $wrapped_char_stream = $get_wrapper->($char_stream);
612      throw Whatpm::HTML::RestartParser (charset => $charset);    $wrapped_char_stream->onerror ($char_onerror);
   }; # $self->{change_encoding}  
613    
614    my @args = @_; shift @args; # $s    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 163  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 176  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    
   my $i = 0;  
681    $self->{line_prev} = $self->{line} = 1;    $self->{line_prev} = $self->{line} = 1;
682    $self->{column_prev} = $self->{column} = 0;    $self->{column_prev} = -1;
683    $self->{set_next_char} = sub {    $self->{column} = 0;
684      $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_char}};      my $char = '';
688      unshift @{$self->{prev_char}}, $self->{next_char};      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_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
697      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          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})      ($self->{line_prev}, $self->{column_prev})
716          = ($self->{line}, $self->{column});          = ($self->{line}, $self->{column});
717      $self->{column}++;      $self->{column}++;
718            
719      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720          !!!cp ('j1');
721        $self->{line}++;        $self->{line}++;
722        $self->{column} = 0;        $self->{column} = 0;
723      } elsif ($self->{next_char} == 0x000D) { # CR      } elsif ($self->{nc} == 0x000D) { # CR
724        $i++ if substr ($$s, $i, 1) eq "\x0A";        !!!cp ('j2');
725        $self->{next_char} = 0x000A; # LF # MUST  ## TODO: support for abort/streaming
726          my $next = '';
727          if ($input->read ($next, 1) and $next ne "\x0A") {
728            $self->{next_nc} = $next;
729          }
730          $self->{nc} = 0x000A; # LF # MUST
731        $self->{line}++;        $self->{line}++;
732        $self->{column} = 0;        $self->{column} = 0;
733      } elsif ($self->{next_char} > 0x10FFFF) {      } elsif ($self->{nc} == 0x0000) { # NULL
734        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        !!!cp ('j4');
     } elsif ($self->{next_char} == 0x0000) { # NULL  
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_char} = [-1, -1, -1];  
740    $self->{next_char} = -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) = @_;
# Line 221  sub parse_string ($$$;$) { Line 783  sub parse_string ($$$;$) {
783      $onerror->(line => $self->{line}, column => $self->{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;
# Line 229  sub parse_string ($$$;$) { Line 805  sub parse_string ($$$;$) {
805    delete $self->{parse_error}; # remove loop    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_char} = sub {      level => {must => 'm',
814      $self->{next_char} = -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 262  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 273  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 295  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 312  sub ROW_IMS ()        { 0b10000000 } Line 914  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 }  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.  ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
# Line 343  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_char}    $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 360  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  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{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 377  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_char} == 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            !!!cp (1);            !!!cp (1);
1071            $self->{state} = ENTITY_DATA_STATE;            ## 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);            !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_char} == 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_char}->[0] == 0x002D and # -            
1088                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1090                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1091                $self->{escape} = 1;              $self->{s_kwd} = '--';
1092              } else {              #
1093                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1094              }              !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097            } else {            } else {
1098              !!!cp (5);              !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_char} == 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})) {
# Line 438  sub _get_next_token ($) { Line 1122  sub _get_next_token ($) {
1122            redo A;            redo A;
1123          } else {          } else {
1124            !!!cp (7);            !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_char} == 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_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1132              !!!cp (8);              !!!cp (8);
1133              delete $self->{escape};              delete $self->{escape};
1134            } else {            } else {
# Line 454  sub _get_next_token ($) { Line 1138  sub _get_next_token ($) {
1138            !!!cp (10);            !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!cp (11);          !!!cp (11);
1145            $self->{s_kwd} = '';
1146          !!!emit ({type => END_OF_FILE_TOKEN,          !!!emit ({type => END_OF_FILE_TOKEN,
1147                    line => $self->{line}, column => $self->{column}});                    line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149        } else {        } else {
1150          !!!cp (12);          !!!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_char},                     data => chr $self->{nc},
1158                     line => $self->{line}, column => $self->{column},                     line => $self->{line}, column => $self->{column},
1159                    };                    };
1160        ## Stay in the data state        if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!next-input-character;                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        !!!emit ($token);        }
   
       redo A;  
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev});  
         
       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          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167          !!!cp (13);          !!!cp (13);
1168          !!!emit ({type => CHARACTER_TOKEN, data => '&',          $self->{state} = PCDATA_STATE;
                   line => $l, column => $c,  
                  });  
1169        } else {        } else {
1170          !!!cp (14);          !!!cp (14);
1171          !!!emit ($token);          ## 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_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1179            !!!cp (15);            !!!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            !!!cp (16);            !!!cp (16);
1189            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           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_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1201            !!!cp (17);            !!!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_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            !!!cp (18);            !!!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_char} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            !!!cp (19);            !!!cp (19);
1213            $self->{current_token}            $self->{ct}
1214              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1215                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1216                 line => $self->{line_prev},                 line => $self->{line_prev},
1217                 column => $self->{column_prev}};                 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_char} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            !!!cp (20);            !!!cp (20);
1224            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1225                                      tag_name => chr ($self->{next_char}),                                      tag_name => chr ($self->{nc}),
1226                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1227                                      column => $self->{column_prev}};                                      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_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!cp (21);            !!!cp (21);
1233            !!!parse-error (type => 'empty start tag',            !!!parse-error (type => 'empty start tag',
1234                            line => $self->{line_prev},                            line => $self->{line_prev},
# Line 560  sub _get_next_token ($) { Line 1242  sub _get_next_token ($) {
1242                     });                     });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!cp (22);            !!!cp (22);
1247            !!!parse-error (type => 'pio',            !!!parse-error (type => 'pio',
1248                            line => $self->{line_prev},                            line => $self->{line_prev},
1249                            column => $self->{column_prev});                            column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            $self->{current_token} = {type => COMMENT_TOKEN, data => '',            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                      line => $self->{line_prev},                                      line => $self->{line_prev},
1253                                      column => $self->{column_prev},                                      column => $self->{column_prev},
1254                                     };                                     };
1255            ## $self->{next_char} is intentionally left as is            ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!cp (23);            !!!cp (23);
1259            !!!parse-error (type => 'bare stago');            !!!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    
# Line 589  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          ## NOTE: The "close tag open state" in the spec is implemented as
1277          ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1278    
1279        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281          if (defined $self->{last_emitted_start_tag_name}) {          if (defined $self->{last_stag_name}) {
1282              $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>            $self->{s_kwd} = '';
1284            my @next_char;            ## Reconsume.
1285            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {            redo A;
             push @next_char, $self->{next_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
   
               !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                         line => $l, column => $c,  
                        });  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_char};  
         
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                       line => $l, column => $c,  
                      });  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = 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              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1290            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1291              ## Reconsume.
1292            !!!emit ({type => CHARACTER_TOKEN, data => '</',            !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                      line => $l, column => $c,                      line => $l, column => $c,
1294                     });                     });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          !!!cp (29);          !!!cp (29);
1302          $self->{current_token}          $self->{ct}
1303              = {type => END_TAG_TOKEN,              = {type => END_TAG_TOKEN,
1304                 tag_name => chr ($self->{next_char} + 0x0020),                 tag_name => chr ($self->{nc} + 0x0020),
1305                 line => $l, column => $c};                 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_char} and        } elsif (0x0061 <= $self->{nc} and
1310                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1311          !!!cp (30);          !!!cp (30);
1312          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1313                                    tag_name => chr ($self->{next_char}),                                    tag_name => chr ($self->{nc}),
1314                                    line => $l, column => $c};                                    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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1319          !!!cp (31);          !!!cp (31);
1320          !!!parse-error (type => 'empty end tag',          !!!parse-error (type => 'empty end tag',
1321                          line => $self->{line_prev}, ## "<" in "</>"                          line => $self->{line_prev}, ## "<" in "</>"
# Line 679  sub _get_next_token ($) { Line 1323  sub _get_next_token ($) {
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_char} == -1) {        } elsif ($self->{nc} == -1) {
1327          !!!cp (32);          !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
# Line 694  sub _get_next_token ($) { Line 1338  sub _get_next_token ($) {
1338          !!!cp (33);          !!!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->{current_token} = {type => COMMENT_TOKEN, data => '',          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342                                    line => $self->{line_prev}, # "<" of "</"                                    line => $self->{line_prev}, # "<" of "</"
1343                                    column => $self->{column_prev} - 1,                                    column => $self->{column_prev} - 1,
1344                                   };                                   };
1345          ## $self->{next_char} is intentionally left as is          ## NOTE: $self->{nc} is intentionally left as is.
1346          redo A;          ## 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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1406          !!!cp (34);          !!!cp (34);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1411          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412            !!!cp (35);            !!!cp (35);
1413            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1418            #  !!! cp (36);            #  !!! cp (36);
1419            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 725  sub _get_next_token ($) { Line 1421  sub _get_next_token ($) {
1421              !!!cp (37);              !!!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_char} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          !!!cp (38);          !!!cp (38);
1435          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $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_char} == -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            !!!cp (39);            !!!cp (39);
1444            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1449            #  !!! cp (40);            #  !!! cp (40);
1450            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 756  sub _get_next_token ($) { Line 1452  sub _get_next_token ($) {
1452              !!!cp (41);              !!!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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (42);  
           #  
         } else {  
           !!!cp (43);  
           !!!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          !!!cp (44);          !!!cp (44);
1470          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1478          !!!cp (45);          !!!cp (45);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            !!!cp (46);            !!!cp (46);
1485            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491            } else {            } else {
1492              !!!cp (48);              !!!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_char} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          !!!cp (49);          !!!cp (49);
1506          $self->{current_attribute}          $self->{ca}
1507              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1508                 value => '',                 value => '',
1509                 line => $self->{line}, column => $self->{column}};                 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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (50);  
           #  
         } else {  
           !!!cp (51);  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_char} == -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            !!!cp (52);            !!!cp (52);
1522            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528            } else {            } else {
1529              !!!cp (54);              !!!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 870  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_char}}) {              }->{$self->{nc}}) {
1546            !!!cp (55);            !!!cp (55);
1547            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1548          } else {          } else {
1549            !!!cp (56);            !!!cp (56);
1550          }          }
1551          $self->{current_attribute}          $self->{ca}
1552              = {name => chr ($self->{next_char}),              = {name => chr ($self->{nc}),
1553                 value => '',                 value => '',
1554                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 886  sub _get_next_token ($) { Line 1558  sub _get_next_token ($) {
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            !!!cp (57);            !!!cp (57);
1564            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1566          } else {          } else {
1567            !!!cp (58);            !!!cp (58);
1568            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569              = $self->{current_attribute};              = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1574          !!!cp (59);          !!!cp (59);
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_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580          !!!cp (60);          !!!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_char} == 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            !!!cp (61);            !!!cp (61);
1589            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591            !!!cp (62);            !!!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_char} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          !!!cp (63);          !!!cp (63);
1608          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (64);  
           #  
         } else {  
           !!!cp (65);  
           !!!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_char} == -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            !!!cp (66);            !!!cp (66);
1623            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629            } else {            } else {
# Line 973  sub _get_next_token ($) { Line 1631  sub _get_next_token ($) {
1631              !!!cp (68);              !!!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_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1644              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1645            !!!cp (69);            !!!cp (69);
1646            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1647          } else {          } else {
1648            !!!cp (70);            !!!cp (70);
1649          }          }
1650          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1657          !!!cp (71);          !!!cp (71);
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_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          !!!cp (72);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            !!!cp (73);            !!!cp (73);
1669            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675            } else {            } else {
# Line 1023  sub _get_next_token ($) { Line 1677  sub _get_next_token ($) {
1677              !!!cp (75);              !!!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_char} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          !!!cp (76);          !!!cp (76);
1691          $self->{current_attribute}          $self->{ca}
1692              = {name => chr ($self->{next_char} + 0x0020),              = {name => chr ($self->{nc} + 0x0020),
1693                 value => '',                 value => '',
1694                 line => $self->{line}, column => $self->{column}};                 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_char} == 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_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (77);  
           #  
         } else {  
           !!!cp (78);  
           !!!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_char} == -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            !!!cp (79);            !!!cp (79);
1707            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713            } else {            } else {
# Line 1072  sub _get_next_token ($) { Line 1715  sub _get_next_token ($) {
1715              !!!cp (81);              !!!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          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1728          $self->{current_attribute}              $self->{nc} == 0x0027) { # '
1729              = {name => chr ($self->{next_char}),            !!!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 => '',                 value => '',
1737                 line => $self->{line}, column => $self->{column}};                 line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
# Line 1091  sub _get_next_token ($) { Line 1740  sub _get_next_token ($) {
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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP        
1744          !!!cp (83);          !!!cp (83);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          !!!cp (84);          !!!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_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          !!!cp (85);          !!!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_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          !!!cp (86);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1765            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766            !!!cp (87);            !!!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);              !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773            } else {            } else {
# Line 1129  sub _get_next_token ($) { Line 1775  sub _get_next_token ($) {
1775              !!!cp (89);              !!!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_char} == -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            !!!cp (90);            !!!cp (90);
1790            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796            } else {            } else {
# Line 1152  sub _get_next_token ($) { Line 1798  sub _get_next_token ($) {
1798              !!!cp (92);              !!!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_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1811            !!!cp (93);            !!!cp (93);
1812            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1813          } else {          } else {
1814            !!!cp (94);            !!!cp (94);
1815          }          }
1816          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          !!!cp (95);          !!!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_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          !!!cp (96);          !!!cp (96);
1829          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1830          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "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_char} == -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            !!!cp (97);            !!!cp (97);
1842            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848            } else {            } else {
# Line 1199  sub _get_next_token ($) { Line 1850  sub _get_next_token ($) {
1850              !!!cp (99);              !!!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          !!!cp (100);          !!!cp (100);
1863          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          !!!cp (101);          !!!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_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1879          !!!cp (102);          !!!cp (102);
1880          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1881          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "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_char} == -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            !!!cp (103);            !!!cp (103);
1893            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899            } else {            } else {
# Line 1241  sub _get_next_token ($) { Line 1901  sub _get_next_token ($) {
1901              !!!cp (105);              !!!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          !!!cp (106);          !!!cp (106);
1914          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # HT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1925          !!!cp (107);          !!!cp (107);
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_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!cp (108);          !!!cp (108);
1931          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1932          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1933            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934            ## implementation of the "consume a character reference" algorithm.
1935            $self->{entity_add} = -1;
1936            $self->{prev_state} = $self->{state};
1937            $self->{state} = ENTITY_STATE;
1938          !!!next-input-character;          !!!next-input-character;
1939          redo A;          redo A;
1940        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1941          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            !!!cp (109);            !!!cp (109);
1943            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1944          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } 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);              !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949            } else {            } else {
# Line 1286  sub _get_next_token ($) { Line 1951  sub _get_next_token ($) {
1951              !!!cp (111);              !!!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_char} == -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            !!!cp (112);            !!!cp (112);
1966            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972            } else {            } else {
# Line 1309  sub _get_next_token ($) { Line 1974  sub _get_next_token ($) {
1974              !!!cp (114);              !!!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 1322  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_char}}) {              }->{$self->{nc}}) {
1991            !!!cp (115);            !!!cp (115);
1992            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1993          } else {          } else {
1994            !!!cp (116);            !!!cp (116);
1995          }          }
1996          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $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) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2007          !!!cp (118);          !!!cp (118);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2012          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013            !!!cp (119);            !!!cp (119);
2014            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2015          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{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);              !!!cp (120);
2019              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2020            } else {            } else {
# Line 1379  sub _get_next_token ($) { Line 2022  sub _get_next_token ($) {
2022              !!!cp (121);              !!!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_char} == 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_char} == 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);            !!!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            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
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);          !!!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} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2067                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068        ## NOTE: Set by the previous state            !!!cp ('124.2');
2069        #my $token = {type => COMMENT_TOKEN, data => ''};            !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071        BC: {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072          if ($self->{next_char} == 0x003E) { # >            if ($self->{ct}->{attributes}) {
2073            !!!cp (124);              !!!cp ('124.4');
2074            $self->{state} = DATA_STATE;              !!!parse-error (type => 'end tag attribute');
2075            !!!next-input-character;            } else {
2076                !!!cp ('124.5');
2077            !!!emit ($self->{current_token}); # comment            }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084            redo A;          $self->{state} = DATA_STATE;
2085          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2086    
2087            !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;          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 {          } else {
2104            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $self->{current_token}->{data} .= chr ($self->{next_char}); # comment  
           !!!next-input-character;  
           redo BC;  
2105          }          }
2106        } # BC          $self->{state} = DATA_STATE;
2107            ## Reconsume.
2108        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2109      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          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) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2120    
2121        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1);        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122          ## consumes characters one-by-one basis.
       my @next_char;  
       push @next_char, $self->{next_char};  
2123                
2124        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127          !!!next-input-character;          !!!next-input-character;
2128          push @next_char, $self->{next_char};  
2129          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2130            !!!cp (127);          redo A;
2131            $self->{current_token} = {type => COMMENT_TOKEN, data => '',        } elsif ($self->{nc} == -1) {
2132                                      line => $l, column => $c,          !!!cp (125);
2133                                     };          $self->{state} = DATA_STATE;
2134            $self->{state} = COMMENT_START_STATE;          ## reconsume
2135            !!!next-input-character;  
2136            redo A;          !!!emit ($self->{ct}); # comment
2137          } else {          redo A;
2138            !!!cp (128);        } else {
2139          }          !!!cp (126);
2140        } elsif ($self->{next_char} == 0x0044 or # D          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141                 $self->{next_char} == 0x0064) { # d          $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## Stay in the state.
2146          !!!next-input-character;          !!!next-input-character;
2147          push @next_char, $self->{next_char};          redo A;
2148          if ($self->{next_char} == 0x004F or # O        }
2149              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150            !!!next-input-character;        ## (only happen if PCDATA state)
2151            push @next_char, $self->{next_char};        
2152            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2153                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2154              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2155              push @next_char, $self->{next_char};          !!!next-input-character;
2156              if ($self->{next_char} == 0x0054 or # T          redo A;
2157                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2158                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2159                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2160                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2161                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2162                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2163                  push @next_char, $self->{next_char};          !!!next-input-character;
2164                  if ($self->{next_char} == 0x0050 or # P          redo A;
2165                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2168                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2169                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2170                      !!!cp (129);          $self->{s_kwd} = '[';
2171                      ## TODO: What a stupid code this is!          !!!next-input-character;
2172                      $self->{state} = DOCTYPE_STATE;          redo A;
                     $self->{current_token} = {type => DOCTYPE_TOKEN,  
                                               quirks => 1,  
                                               line => $l, column => $c,  
                                              };  
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (130);  
                   }  
                 } else {  
                   !!!cp (131);  
                 }  
               } else {  
                 !!!cp (132);  
               }  
             } else {  
               !!!cp (133);  
             }  
           } else {  
             !!!cp (134);  
           }  
         } else {  
           !!!cp (135);  
         }  
2173        } else {        } else {
2174          !!!cp (136);          !!!cp (136);
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_char} = 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->{current_token} = {type => COMMENT_TOKEN, data => '',        $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                  line => $l, column => $c,                                  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_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2299          !!!cp (137);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2304          !!!cp (138);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2313          !!!cp (139);          !!!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          !!!cp (140);          !!!cp (140);
2323          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2324              .= chr ($self->{next_char});              .= 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_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331          !!!cp (141);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336          !!!cp (142);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2345          !!!cp (143);          !!!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          !!!cp (144);          !!!cp (144);
2355          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2356              .= '-' . chr ($self->{next_char});              .= '-' . 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_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363          !!!cp (145);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2368          !!!cp (146);          !!!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          !!!cp (147);          !!!cp (147);
2378          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $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_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          !!!cp (148);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2394          !!!cp (149);          !!!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          !!!cp (150);          !!!cp (150);
2404          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $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_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          !!!cp (151);          !!!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_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!cp (152);          !!!cp (152);
2420          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2421                          line => $self->{line_prev},                          line => $self->{line_prev},
2422                          column => $self->{column_prev});                          column => $self->{column_prev});
2423          $self->{current_token}->{data} .= '-'; # comment          $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_char} == -1) {        } elsif ($self->{nc} == -1) {
2428          !!!cp (153);          !!!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 {
# Line 1671  sub _get_next_token ($) { Line 2438  sub _get_next_token ($) {
2438          !!!parse-error (type => 'dash in comment',          !!!parse-error (type => 'dash in comment',
2439                          line => $self->{line_prev},                          line => $self->{line_prev},
2440                          column => $self->{column_prev});                          column => $self->{column_prev});
2441          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment          $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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2448          !!!cp (155);          !!!cp (155);
2449          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2450          !!!next-input-character;          !!!next-input-character;
# Line 1694  sub _get_next_token ($) { Line 2457  sub _get_next_token ($) {
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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2461          !!!cp (157);          !!!cp (157);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466          !!!cp (158);          !!!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 ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2475          !!!cp (159);          !!!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 ($self->{current_token}); # DOCTYPE (quirks)          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          !!!cp (160);          !!!cp (160);
2485          $self->{current_token}->{name} = chr $self->{next_char};          $self->{ct}->{name} = chr $self->{nc};
2486          delete $self->{current_token}->{quirks};          delete $self->{ct}->{quirks};
 ## 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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2494          !!!cp (161);          !!!cp (161);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          !!!cp (162);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!cp (163);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          !!!cp (164);          !!!cp (164);
2518          $self->{current_token}->{name}          $self->{ct}->{name}
2519            .= chr ($self->{next_char}); # DOCTYPE            .= 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_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2526          !!!cp (165);          !!!cp (165);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          !!!cp (166);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2539          !!!cp (167);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_char} == 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_char} == 0x0055 or # U          redo A;
2554              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2560        } else {        } else {
2561          !!!cp (180);          !!!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        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2605        $self->{current_token}->{quirks} = 1;          ## 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        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        # next-input-character is already done          ## Reconsume.
2645        redo A;          redo A;
2646          }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2649          !!!cp (181);          !!!cp (181);
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_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          !!!cp (182);          !!!cp (182);
2655          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $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_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          !!!cp (183);          !!!cp (183);
2661          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $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_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666          !!!cp (184);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2677          !!!cp (185);          !!!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          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{current_token}->{quirks} = 1;          $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_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          !!!cp (187);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2703          !!!cp (188);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2710          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2714          !!!cp (189);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          !!!cp (190);          !!!cp (190);
2726          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2727              .= chr $self->{next_char};              .= 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_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          !!!cp (191);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2742          !!!cp (192);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2749          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2750    
2751          redo A;          redo A;
2752        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2753          !!!cp (193);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          !!!cp (194);          !!!cp (194);
2765          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2766              .= chr $self->{next_char};              .= 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}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2776          !!!cp (195);          !!!cp (195);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          !!!cp (196);          !!!cp (196);
2782          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $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_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          !!!cp (197);          !!!cp (197);
2788          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          !!!cp (198);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2801          !!!cp (199);          !!!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          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{current_token}->{quirks} = 1;          $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}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2822          !!!cp (201);          !!!cp (201);
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_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          !!!cp (202);          !!!cp (202);
2828          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $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_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          !!!cp (203);          !!!cp (203);
2834          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839          !!!cp (204);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2849          !!!cp (205);          !!!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          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862          $self->{current_token}->{quirks} = 1;          $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_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          !!!cp (207);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2875          !!!cp (208);          !!!cp (208);
2876          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2882          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2886          !!!cp (209);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          !!!cp (210);          !!!cp (210);
2898          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2899              .= chr $self->{next_char};              .= 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_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          !!!cp (211);          !!!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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2914          !!!cp (212);          !!!cp (212);
2915          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2921          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2922    
2923          redo A;          redo A;
2924        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2925          !!!cp (213);          !!!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          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          !!!cp (214);          !!!cp (214);
2937          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2938              .= chr $self->{next_char};              .= 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}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2948          !!!cp (215);          !!!cp (215);
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_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          !!!cp (216);          !!!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_char} == -1) {        } elsif ($self->{nc} == -1) {
2961          !!!cp (217);          !!!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          $self->{current_token}->{quirks} = 1;          $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);          !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          #$self->{current_token}->{quirks} = 1;          #$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_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          !!!cp (219);          !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2989          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2990          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2991          ## reconsume          ## reconsume
2992    
2993          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2994    
2995          redo A;          redo A;
2996        } else {        } else {
2997          !!!cp (221);          !!!cp (221);
2998            my $s = '';
2999            $self->{read_until}->($s, q[>], 0);
3000    
3001          ## Stay in the state          ## Stay in the state
3002          !!!next-input-character;          !!!next-input-character;
3003          redo A;          redo A;
3004        }        }
3005      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3006        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3007      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3008    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3009          
3010          if ($self->{nc} == 0x005D) { # ]
3011            !!!cp (221.1);
3012            $self->{state} = CDATA_SECTION_MSE1_STATE;
3013            !!!next-input-character;
3014            redo A;
3015          } elsif ($self->{nc} == -1) {
3016            $self->{state} = DATA_STATE;
3017            !!!next-input-character;
3018            if (length $self->{ct}->{data}) { # character
3019              !!!cp (221.2);
3020              !!!emit ($self->{ct}); # character
3021            } else {
3022              !!!cp (221.3);
3023              ## No token to emit. $self->{ct} is discarded.
3024            }        
3025            redo A;
3026          } else {
3027            !!!cp (221.4);
3028            $self->{ct}->{data} .= chr $self->{nc};
3029            $self->{read_until}->($self->{ct}->{data},
3030                                  q<]>,
3031                                  length $self->{ct}->{data});
3032    
3033    die "$0: _get_next_token: unexpected case";          ## Stay in the state.
3034  } # _get_next_token          !!!next-input-character;
3035            redo A;
3036          }
3037    
3038  sub _tokenize_attempt_to_consume_an_entity ($$$) {        ## ISSUE: "text tokens" in spec.
3039    my ($self, $in_attr, $additional) = @_;      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3040          if ($self->{nc} == 0x005D) { # ]
3041            !!!cp (221.5);
3042            $self->{state} = CDATA_SECTION_MSE2_STATE;
3043            !!!next-input-character;
3044            redo A;
3045          } else {
3046            !!!cp (221.6);
3047            $self->{ct}->{data} .= ']';
3048            $self->{state} = CDATA_SECTION_STATE;
3049            ## Reconsume.
3050            redo A;
3051          }
3052        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3053          if ($self->{nc} == 0x003E) { # >
3054            $self->{state} = DATA_STATE;
3055            !!!next-input-character;
3056            if (length $self->{ct}->{data}) { # character
3057              !!!cp (221.7);
3058              !!!emit ($self->{ct}); # character
3059            } else {
3060              !!!cp (221.8);
3061              ## No token to emit. $self->{ct} is discarded.
3062            }
3063            redo A;
3064          } elsif ($self->{nc} == 0x005D) { # ]
3065            !!!cp (221.9); # character
3066            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3067            ## Stay in the state.
3068            !!!next-input-character;
3069            redo A;
3070          } else {
3071            !!!cp (221.11);
3072            $self->{ct}->{data} .= ']]'; # character
3073            $self->{state} = CDATA_SECTION_STATE;
3074            ## Reconsume.
3075            redo A;
3076          }
3077        } elsif ($self->{state} == ENTITY_STATE) {
3078          if ($is_space->{$self->{nc}} or
3079              {
3080                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3081                $self->{entity_add} => 1,
3082              }->{$self->{nc}}) {
3083            !!!cp (1001);
3084            ## Don't consume
3085            ## No error
3086            ## Return nothing.
3087            #
3088          } elsif ($self->{nc} == 0x0023) { # #
3089            !!!cp (999);
3090            $self->{state} = ENTITY_HASH_STATE;
3091            $self->{s_kwd} = '#';
3092            !!!next-input-character;
3093            redo A;
3094          } elsif ((0x0041 <= $self->{nc} and
3095                    $self->{nc} <= 0x005A) or # A..Z
3096                   (0x0061 <= $self->{nc} and
3097                    $self->{nc} <= 0x007A)) { # a..z
3098            !!!cp (998);
3099            require Whatpm::_NamedEntityList;
3100            $self->{state} = ENTITY_NAME_STATE;
3101            $self->{s_kwd} = chr $self->{nc};
3102            $self->{entity__value} = $self->{s_kwd};
3103            $self->{entity__match} = 0;
3104            !!!next-input-character;
3105            redo A;
3106          } else {
3107            !!!cp (1027);
3108            !!!parse-error (type => 'bare ero');
3109            ## Return nothing.
3110            #
3111          }
3112    
3113    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        ## NOTE: No character is consumed by the "consume a character
3114          ## reference" algorithm.  In other word, there is an "&" character
3115          ## that does not introduce a character reference, which would be
3116          ## appended to the parent element or the attribute value in later
3117          ## process of the tokenizer.
3118    
3119          if ($self->{prev_state} == DATA_STATE) {
3120            !!!cp (997);
3121            $self->{state} = $self->{prev_state};
3122            ## Reconsume.
3123            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3124                      line => $self->{line_prev},
3125                      column => $self->{column_prev},
3126                     });
3127            redo A;
3128          } else {
3129            !!!cp (996);
3130            $self->{ca}->{value} .= '&';
3131            $self->{state} = $self->{prev_state};
3132            ## Reconsume.
3133            redo A;
3134          }
3135        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3136          if ($self->{nc} == 0x0078 or # x
3137              $self->{nc} == 0x0058) { # X
3138            !!!cp (995);
3139            $self->{state} = HEXREF_X_STATE;
3140            $self->{s_kwd} .= chr $self->{nc};
3141            !!!next-input-character;
3142            redo A;
3143          } elsif (0x0030 <= $self->{nc} and
3144                   $self->{nc} <= 0x0039) { # 0..9
3145            !!!cp (994);
3146            $self->{state} = NCR_NUM_STATE;
3147            $self->{s_kwd} = $self->{nc} - 0x0030;
3148            !!!next-input-character;
3149            redo A;
3150          } else {
3151            !!!parse-error (type => 'bare nero',
3152                            line => $self->{line_prev},
3153                            column => $self->{column_prev} - 1);
3154    
3155    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3156         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3157         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3158         $additional => 1,  
3159        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3160      !!!cp (1001);            !!!cp (1019);
3161      ## Don't consume            $self->{state} = $self->{prev_state};
3162      ## No error            ## Reconsume.
3163      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3164    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3165      !!!next-input-character;                      line => $self->{line_prev},
3166      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3167          $self->{next_char} == 0x0058) { # X                     });
3168        my $code;            redo A;
       X: {  
         my $x_char = $self->{next_char};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_char} and  
             $self->{next_char} <= 0x0039) { # 0..9  
           !!!cp (1002);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           !!!cp (1003);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           !!!cp (1004);  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!cp (1005);  
           !!!parse-error (type => 'bare hcro', line => $l, column => $c);  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!cp (1006);  
           !!!next-input-character;  
3169          } else {          } else {
3170            !!!cp (1007);            !!!cp (993);
3171            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3172              $self->{state} = $self->{prev_state};
3173              ## Reconsume.
3174              redo A;
3175          }          }
3176          }
3177          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3178            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3179            !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);            $self->{nc} <= 0x0039) { # 0..9
           $code = 0xFFFD;  
         } elsif ($code > 0x10FFFF) {  
           !!!cp (1009);  
           !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!cp (1010);  
           !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!cp (1011);  
           !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1,  
                 line => $l, column => $c,  
                };  
       } # X  
     } elsif (0x0030 <= $self->{next_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
3180          !!!cp (1012);          !!!cp (1012);
3181          $code *= 10;          $self->{s_kwd} *= 10;
3182          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3183                    
3184            ## Stay in the state.
3185          !!!next-input-character;          !!!next-input-character;
3186        }          redo A;
3187          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3188          !!!cp (1013);          !!!cp (1013);
3189          !!!next-input-character;          !!!next-input-character;
3190            #
3191        } else {        } else {
3192          !!!cp (1014);          !!!cp (1014);
3193          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3194            ## Reconsume.
3195            #
3196        }        }
3197    
3198        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3199          my $l = $self->{line_prev};
3200          my $c = $self->{column_prev};
3201          if ($charref_map->{$code}) {
3202          !!!cp (1015);          !!!cp (1015);
3203          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3204          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3205                            line => $l, column => $c);
3206            $code = $charref_map->{$code};
3207        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3208          !!!cp (1016);          !!!cp (1016);
3209          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3210                            text => (sprintf 'U-%08X', $code),
3211                            line => $l, column => $c);
3212          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference', line => $l, column => $c);  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => (sprintf 'C1 character reference:U+%04X', $code), line => $l, column => $c);  
         $code = $c1_entity_char->{$code};  
3213        }        }
3214          
3215        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3216                line => $l, column => $c,          !!!cp (992);
3217               };          $self->{state} = $self->{prev_state};
3218      } else {          ## Reconsume.
3219        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3220        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3221        !!!back-next-input-character ($self->{next_char});                   });
3222        $self->{next_char} = 0x0023; # #          redo A;
3223        return undef;        } else {
3224      }          !!!cp (991);
3225    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3226              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3227             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3228              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3229      my $entity_name = chr $self->{next_char};          redo A;
3230      !!!next-input-character;        }
3231        } elsif ($self->{state} == HEXREF_X_STATE) {
3232      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3233      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3234      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3235      our $EntityChar;          # 0..9, A..F, a..f
3236            !!!cp (990);
3237      while (length $entity_name < 10 and          $self->{state} = HEXREF_HEX_STATE;
3238             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3239             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3240               $self->{next_char} <= 0x005A) or # x          redo A;
3241              (0x0061 <= $self->{next_char} and # a        } else {
3242               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3243              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3244               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3245              $self->{next_char} == 0x003B)) { # ;  
3246        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3247        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3248          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3249            !!!cp (1020);  
3250            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3251            $match = 1;            !!!cp (1005);
3252            !!!next-input-character;            $self->{state} = $self->{prev_state};
3253            last;            ## Reconsume.
3254              !!!emit ({type => CHARACTER_TOKEN,
3255                        data => '&' . $self->{s_kwd},
3256                        line => $self->{line_prev},
3257                        column => $self->{column_prev} - length $self->{s_kwd},
3258                       });
3259              redo A;
3260            } else {
3261              !!!cp (989);
3262              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3263              $self->{state} = $self->{prev_state};
3264              ## Reconsume.
3265              redo A;
3266            }
3267          }
3268        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3269          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3270            # 0..9
3271            !!!cp (1002);
3272            $self->{s_kwd} *= 0x10;
3273            $self->{s_kwd} += $self->{nc} - 0x0030;
3274            ## Stay in the state.
3275            !!!next-input-character;
3276            redo A;
3277          } elsif (0x0061 <= $self->{nc} and
3278                   $self->{nc} <= 0x0066) { # a..f
3279            !!!cp (1003);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0041 <= $self->{nc} and
3286                   $self->{nc} <= 0x0046) { # A..F
3287            !!!cp (1004);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif ($self->{nc} == 0x003B) { # ;
3294            !!!cp (1006);
3295            !!!next-input-character;
3296            #
3297          } else {
3298            !!!cp (1007);
3299            !!!parse-error (type => 'no refc',
3300                            line => $self->{line},
3301                            column => $self->{column});
3302            ## Reconsume.
3303            #
3304          }
3305    
3306          my $code = $self->{s_kwd};
3307          my $l = $self->{line_prev};
3308          my $c = $self->{column_prev};
3309          if ($charref_map->{$code}) {
3310            !!!cp (1008);
3311            !!!parse-error (type => 'invalid character reference',
3312                            text => (sprintf 'U+%04X', $code),
3313                            line => $l, column => $c);
3314            $code = $charref_map->{$code};
3315          } elsif ($code > 0x10FFFF) {
3316            !!!cp (1009);
3317            !!!parse-error (type => 'invalid character reference',
3318                            text => (sprintf 'U-%08X', $code),
3319                            line => $l, column => $c);
3320            $code = 0xFFFD;
3321          }
3322    
3323          if ($self->{prev_state} == DATA_STATE) {
3324            !!!cp (988);
3325            $self->{state} = $self->{prev_state};
3326            ## Reconsume.
3327            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3328                      line => $l, column => $c,
3329                     });
3330            redo A;
3331          } else {
3332            !!!cp (987);
3333            $self->{ca}->{value} .= chr $code;
3334            $self->{ca}->{has_reference} = 1;
3335            $self->{state} = $self->{prev_state};
3336            ## Reconsume.
3337            redo A;
3338          }
3339        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3340          if (length $self->{s_kwd} < 30 and
3341              ## NOTE: Some number greater than the maximum length of entity name
3342              ((0x0041 <= $self->{nc} and # a
3343                $self->{nc} <= 0x005A) or # x
3344               (0x0061 <= $self->{nc} and # a
3345                $self->{nc} <= 0x007A) or # z
3346               (0x0030 <= $self->{nc} and # 0
3347                $self->{nc} <= 0x0039) or # 9
3348               $self->{nc} == 0x003B)) { # ;
3349            our $EntityChar;
3350            $self->{s_kwd} .= chr $self->{nc};
3351            if (defined $EntityChar->{$self->{s_kwd}}) {
3352              if ($self->{nc} == 0x003B) { # ;
3353                !!!cp (1020);
3354                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3355                $self->{entity__match} = 1;
3356                !!!next-input-character;
3357                #
3358              } else {
3359                !!!cp (1021);
3360                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3361                $self->{entity__match} = -1;
3362                ## Stay in the state.
3363                !!!next-input-character;
3364                redo A;
3365              }
3366          } else {          } else {
3367            !!!cp (1021);            !!!cp (1022);
3368            $value = $EntityChar->{$entity_name};            $self->{entity__value} .= chr $self->{nc};
3369            $match = -1;            $self->{entity__match} *= 2;
3370              ## Stay in the state.
3371            !!!next-input-character;            !!!next-input-character;
3372              redo A;
3373            }
3374          }
3375    
3376          my $data;
3377          my $has_ref;
3378          if ($self->{entity__match} > 0) {
3379            !!!cp (1023);
3380            $data = $self->{entity__value};
3381            $has_ref = 1;
3382            #
3383          } elsif ($self->{entity__match} < 0) {
3384            !!!parse-error (type => 'no refc');
3385            if ($self->{prev_state} != DATA_STATE and # in attribute
3386                $self->{entity__match} < -1) {
3387              !!!cp (1024);
3388              $data = '&' . $self->{s_kwd};
3389              #
3390            } else {
3391              !!!cp (1025);
3392              $data = $self->{entity__value};
3393              $has_ref = 1;
3394              #
3395          }          }
3396        } else {        } else {
3397          !!!cp (1022);          !!!cp (1026);
3398          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3399          $match *= 2;                          line => $self->{line_prev},
3400          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3401            $data = '&' . $self->{s_kwd};
3402            #
3403        }        }
3404      }    
3405              ## NOTE: In these cases, when a character reference is found,
3406      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3407        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3408        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3409                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3410               };        ## as string, either literal string when no character reference or
3411      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3412        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3413        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3414          !!!cp (1024);  
3415          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3416                  line => $l, column => $c,          !!!cp (986);
3417                 };          $self->{state} = $self->{prev_state};
3418        } else {          ## Reconsume.
3419          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3420          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3421                  line => $l, column => $c,                    line => $self->{line_prev},
3422                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3423                     });
3424            redo A;
3425          } else {
3426            !!!cp (985);
3427            $self->{ca}->{value} .= $data;
3428            $self->{ca}->{has_reference} = 1 if $has_ref;
3429            $self->{state} = $self->{prev_state};
3430            ## Reconsume.
3431            redo A;
3432        }        }
3433      } else {      } else {
3434        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero', line => $l, column => $c);  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value,  
               line => $l, column => $c,  
              };  
3435      }      }
3436    } else {    } # A  
3437      !!!cp (1027);  
3438      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3439      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3440    
3441  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3442    my $self = shift;    my $self = shift;
# Line 2462  sub _initialize_tree_constructor ($) { Line 3445  sub _initialize_tree_constructor ($) {
3445    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3446    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3447    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3448      $self->{document}->set_user_data (manakai_source_line => 1);
3449      $self->{document}->set_user_data (manakai_source_column => 1);
3450  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3451    
3452  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2490  sub _construct_tree ($) { Line 3475  sub _construct_tree ($) {
3475    
3476    undef $self->{form_element};    undef $self->{form_element};
3477    undef $self->{head_element};    undef $self->{head_element};
3478      undef $self->{head_element_inserted};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
# Line 2516  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}) {
           defined $token->{system_identifier}) {  
3508          !!!cp ('t1');          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          !!!cp ('t2');          !!!cp ('t2');
         ## ISSUE: ASCII case-insensitive? (in fact it does not matter)  
3512          !!!parse-error (type => 'not HTML5', token => $token);          !!!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 {        } else {
3522          !!!cp ('t3');          !!!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);
# Line 2543  sub _tree_construction_initial ($) { Line 3536  sub _tree_construction_initial ($) {
3536        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537          !!!cp ('t4');          !!!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") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//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}) {  
3610            !!!cp ('t5');            !!!cp ('t5');
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');              !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618              !!!cp ('t7');              !!!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');            !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625          } else {          } else {
# Line 2641  sub _tree_construction_initial ($) { Line 3628  sub _tree_construction_initial ($) {
3628        } else {        } else {
3629          !!!cp ('t10');          !!!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            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## 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');            !!!cp ('t11');
3639          } else {          } else {
# Line 2668  sub _tree_construction_initial ($) { Line 3656  sub _tree_construction_initial ($) {
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the "before html" insertion mode.        ## 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}) {
# Line 2727  sub _tree_construction_root_element ($) Line 3716  sub _tree_construction_root_element ($)
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}) {
# Line 2748  sub _tree_construction_root_element ($) Line 3737  sub _tree_construction_root_element ($)
3737        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3738          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3739            my $root_element;            my $root_element;
3740            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes}, $token);            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3742            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3746              !!!cp ('t24');              !!!cp ('t24');
# Line 2765  sub _tree_construction_root_element ($) Line 3755  sub _tree_construction_root_element ($)
3755              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3756            }            }
3757    
3758              !!!nack ('t25c');
3759    
3760            !!!next-token;            !!!next-token;
3761            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3762          } else {          } else {
# Line 2781  sub _tree_construction_root_element ($) Line 3773  sub _tree_construction_root_element ($)
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',, $token);      my $root_element;
3777        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3779      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780    
3781      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3782    
3783      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3786    } # B    } # B
3787    
3788    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2811  sub _reset_insertion_mode ($) { Line 3803  sub _reset_insertion_mode ($) {
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              !!!cp ('t27');          } else {
3809              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $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                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3834                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
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 2839  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');            !!!cp ('t29');
3853            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2857  sub _reset_insertion_mode ($) { Line 3861  sub _reset_insertion_mode ($) {
3861          !!!cp ('t31');          !!!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    
# Line 2976  sub _tree_construction_main ($) { Line 3980  sub _tree_construction_main ($) {
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}, $token);      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3984    
3985      ## Step 2      ## Step 2
3986      $insert->($el);      $insert->($el);
# Line 2987  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');        !!!cp ('t40');
# Line 3013  sub _tree_construction_main ($) { Line 4018  sub _tree_construction_main ($) {
4018        ## NOTE: An end-of-file token.        ## NOTE: An end-of-file token.
4019        if ($content_model_flag == CDATA_CONTENT_MODEL) {        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4020          !!!cp ('t43');          !!!cp ('t43');
4021          !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4022        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {        } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4023          !!!cp ('t44');          !!!cp ('t44');
4024          !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);          !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4025        } else {        } else {
4026          die "$0: $content_model_flag in parse_rcdata";          die "$0: $content_model_flag in parse_rcdata";
4027        }        }
# Line 3026  sub _tree_construction_main ($) { Line 4031  sub _tree_construction_main ($) {
4031    
4032    my $script_start_tag = sub () {    my $script_start_tag = sub () {
4033      my $script_el;      my $script_el;
4034      !!!create-element ($script_el, 'script', $token->{attributes}, $token);      !!!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');        !!!cp ('t45');
# Line 3052  sub _tree_construction_main ($) { Line 4058  sub _tree_construction_main ($) {
4058        ## Ignore the token        ## Ignore the token
4059      } else {      } else {
4060        !!!cp ('t48');        !!!cp ('t48');
4061        !!!parse-error (type => 'in CDATA:#'.$token->{type}, token => $token);        !!!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      }      }
# Line 3077  sub _tree_construction_main ($) { Line 4083  sub _tree_construction_main ($) {
4083    
4084    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4085    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4086      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4087    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4088    
4089    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 3090  sub _tree_construction_main ($) { Line 4097  sub _tree_construction_main ($) {
4097        my $formatting_element;        my $formatting_element;
4098        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4099        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4100          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4101              !!!cp ('t52');
4102              last AFE;
4103            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4104                         eq $tag_name) {
4105            !!!cp ('t51');            !!!cp ('t51');
4106            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4107            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4108            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4109          }          }
4110        } # AFE        } # AFE
4111        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4112          !!!cp ('t53');          !!!cp ('t53');
4113          !!!parse-error (type => 'unmatched end tag:'.$tag_name, token => $end_tag_token);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4114          ## Ignore the token          ## Ignore the token
4115          !!!next-token;          !!!next-token;
4116          return;          return;
# Line 3119  sub _tree_construction_main ($) { Line 4127  sub _tree_construction_main ($) {
4127              last INSCOPE;              last INSCOPE;
4128            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4129              !!!cp ('t55');              !!!cp ('t55');
4130              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4131                                text => $token->{tag_name},
4132                              token => $end_tag_token);                              token => $end_tag_token);
4133              ## Ignore the token              ## Ignore the token
4134              !!!next-token;              !!!next-token;
4135              return;              return;
4136            }            }
4137          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4138            !!!cp ('t56');            !!!cp ('t56');
4139            $in_scope = 0;            $in_scope = 0;
4140          }          }
4141        } # INSCOPE        } # INSCOPE
4142        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4143          !!!cp ('t57');          !!!cp ('t57');
4144          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4145                            text => $token->{tag_name},
4146                          token => $end_tag_token);                          token => $end_tag_token);
4147          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4148          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3143  sub _tree_construction_main ($) { Line 4150  sub _tree_construction_main ($) {
4150        }        }
4151        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4152          !!!cp ('t58');          !!!cp ('t58');
4153          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1],          !!!parse-error (type => 'not closed',
4154                            text => $self->{open_elements}->[-1]->[0]
4155                                ->manakai_local_name,
4156                          token => $end_tag_token);                          token => $end_tag_token);
4157        }        }
4158                
# Line 3152  sub _tree_construction_main ($) { Line 4161  sub _tree_construction_main ($) {
4161        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4162        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4163          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4164          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4165              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4166              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4167               $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4168            !!!cp ('t59');            !!!cp ('t59');
4169            $furthest_block = $node;            $furthest_block = $node;
4170            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4171              ## NOTE: The topmost (eldest) node.
4172          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4173            !!!cp ('t60');            !!!cp ('t60');
4174            last OE;            last OE;
# Line 3241  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        } # S7          } # S7  
4252                
4253        ## Step 8        ## Step 8
4254        if ({        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
            table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
           }->{$common_ancestor_node->[1]}) {  
4255          my $foster_parent_element;          my $foster_parent_element;
4256          my $next_sibling;          my $next_sibling;
4257                           OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4258                             if ($self->{open_elements}->[$_]->[1] eq 'table') {            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4259                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4260                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4261                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3307  sub _tree_construction_main ($) { Line 4315  sub _tree_construction_main ($) {
4315            $i = $_;            $i = $_;
4316          }          }
4317        } # OE        } # OE
4318        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4319                
4320        ## Step 14        ## Step 14
4321        redo FET;        redo FET;
# Line 3320  sub _tree_construction_main ($) { Line 4328  sub _tree_construction_main ($) {
4328    
4329    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4330      my $child = shift;      my $child = shift;
4331      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]}) {  
4332        # MUST        # MUST
4333        my $foster_parent_element;        my $foster_parent_element;
4334        my $next_sibling;        my $next_sibling;
4335                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4336                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4337                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4338                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4339                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3352  sub _tree_construction_main ($) { Line 4358  sub _tree_construction_main ($) {
4358      }      }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361    B: {    ## NOTE: When a character is inserted, if the last node that was
4362      ## inserted by the parser is a Text node and the character has to be
4363      ## inserted after that node, then the character is appended to the
4364      ## Text node.  However, if any other node is inserted by the parser,
4365      ## then a new Text node is created and the character is appended as
4366      ## that Text node.  If I'm not wrong, there are only two cases where
4367      ## this occurs.  One is the case where an element node is inserted
4368      ## to the |head| element.  This is covered by using the
4369      ## |$self->{head_element_inserted}| flag.  Another is the case where
4370      ## an element or comment is inserted into the |table| subtree while
4371      ## foster parenting happens.  This is covered by using the [2] flag
4372      ## of the |$open_tables| structure.  All other cases are handled
4373      ## simply by calling |manakai_append_text| method.
4374    
4375      B: while (1) {
4376      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4377        !!!cp ('t73');        !!!cp ('t73');
4378        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4379        ## Ignore the token        ## Ignore the token
4380        ## Stay in the phase        ## Stay in the phase
4381        !!!next-token;        !!!next-token;
4382        redo B;        next B;
4383      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4384               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4385        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4386          !!!cp ('t79');          !!!cp ('t79');
4387          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4388          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4389        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4390          !!!cp ('t80');          !!!cp ('t80');
4391          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4392          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4393        } else {        } else {
4394          !!!cp ('t81');          !!!cp ('t81');
# Line 3385  sub _tree_construction_main ($) { Line 4405  sub _tree_construction_main ($) {
4405               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4406          }          }
4407        }        }
4408          !!!nack ('t84.1');
4409        !!!next-token;        !!!next-token;
4410        redo B;        next B;
4411      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4412        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4413        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3398  sub _tree_construction_main ($) { Line 4419  sub _tree_construction_main ($) {
4419        } else {        } else {
4420          !!!cp ('t87');          !!!cp ('t87');
4421          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4422            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4423        }        }
4424        !!!next-token;        !!!next-token;
4425        redo B;        next B;
4426      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4427          if ($token->{type} == CHARACTER_TOKEN) {
4428            !!!cp ('t87.1');
4429            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4430            !!!next-token;
4431            next B;
4432          } elsif ($token->{type} == START_TAG_TOKEN) {
4433            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4434                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4435                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4436                ($token->{tag_name} eq 'svg' and
4437                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4438              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4439              !!!cp ('t87.2');
4440              #
4441            } elsif ({
4442                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4443                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4444                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4445                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4446                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4447                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4448                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4449                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4450                     }->{$token->{tag_name}}) {
4451              !!!cp ('t87.2');
4452              !!!parse-error (type => 'not closed',
4453                              text => $self->{open_elements}->[-1]->[0]
4454                                  ->manakai_local_name,
4455                              token => $token);
4456    
4457              pop @{$self->{open_elements}}
4458                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4459    
4460              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4461              ## Reprocess.
4462              next B;
4463            } else {
4464              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4465              my $tag_name = $token->{tag_name};
4466              if ($nsuri eq $SVG_NS) {
4467                $tag_name = {
4468                   altglyph => 'altGlyph',
4469                   altglyphdef => 'altGlyphDef',
4470                   altglyphitem => 'altGlyphItem',
4471                   animatecolor => 'animateColor',
4472                   animatemotion => 'animateMotion',
4473                   animatetransform => 'animateTransform',
4474                   clippath => 'clipPath',
4475                   feblend => 'feBlend',
4476                   fecolormatrix => 'feColorMatrix',
4477                   fecomponenttransfer => 'feComponentTransfer',
4478                   fecomposite => 'feComposite',
4479                   feconvolvematrix => 'feConvolveMatrix',
4480                   fediffuselighting => 'feDiffuseLighting',
4481                   fedisplacementmap => 'feDisplacementMap',
4482                   fedistantlight => 'feDistantLight',
4483                   feflood => 'feFlood',
4484                   fefunca => 'feFuncA',
4485                   fefuncb => 'feFuncB',
4486                   fefuncg => 'feFuncG',
4487                   fefuncr => 'feFuncR',
4488                   fegaussianblur => 'feGaussianBlur',
4489                   feimage => 'feImage',
4490                   femerge => 'feMerge',
4491                   femergenode => 'feMergeNode',
4492                   femorphology => 'feMorphology',
4493                   feoffset => 'feOffset',
4494                   fepointlight => 'fePointLight',
4495                   fespecularlighting => 'feSpecularLighting',
4496                   fespotlight => 'feSpotLight',
4497                   fetile => 'feTile',
4498                   feturbulence => 'feTurbulence',
4499                   foreignobject => 'foreignObject',
4500                   glyphref => 'glyphRef',
4501                   lineargradient => 'linearGradient',
4502                   radialgradient => 'radialGradient',
4503                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4504                   textpath => 'textPath',  
4505                }->{$tag_name} || $tag_name;
4506              }
4507    
4508              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4509    
4510              ## "adjust foreign attributes" - done in insert-element-f
4511    
4512              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4513    
4514              if ($self->{self_closing}) {
4515                pop @{$self->{open_elements}};
4516                !!!ack ('t87.3');
4517              } else {
4518                !!!cp ('t87.4');
4519              }
4520    
4521              !!!next-token;
4522              next B;
4523            }
4524          } elsif ($token->{type} == END_TAG_TOKEN) {
4525            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4526            !!!cp ('t87.5');
4527            #
4528          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4529            !!!cp ('t87.6');
4530            !!!parse-error (type => 'not closed',
4531                            text => $self->{open_elements}->[-1]->[0]
4532                                ->manakai_local_name,
4533                            token => $token);
4534    
4535            pop @{$self->{open_elements}}
4536                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4537    
4538            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4539    
4540            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4541            ## Reprocess.
4542            next B;
4543          } else {
4544            die "$0: $token->{type}: Unknown token type";        
4545          }
4546        }
4547    
4548        if ($self->{insertion_mode} & HEAD_IMS) {
4549        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4550          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4551            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4553              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4554                  $self->{open_elements}->[-1]->[0]->append_child
4555                    ($self->{document}->create_text_node ($1));
4556                  delete $self->{head_element_inserted};
4557                  ## NOTE: |</head> <link> |
4558                  #
4559                } else {
4560                  !!!cp ('t88.2');
4561                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4562                  ## NOTE: |</head> &#x20;|
4563                  #
4564                }
4565            } else {            } else {
4566              !!!cp ('t88.1');              !!!cp ('t88.1');
4567              ## Ignore the token.              ## Ignore the token.
4568              !!!next-token;              #
             redo B;  
4569            }            }
4570            unless (length $token->{data}) {            unless (length $token->{data}) {
4571              !!!cp ('t88');              !!!cp ('t88');
4572              !!!next-token;              !!!next-token;
4573              redo B;              next B;
4574            }            }
4575    ## TODO: set $token->{column} appropriately
4576          }          }
4577    
4578          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4579            !!!cp ('t89');            !!!cp ('t89');
4580            ## As if <head>            ## As if <head>
4581            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4582            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4583            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4584                  [$self->{head_element}, $el_category->{head}];
4585    
4586            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4587            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3435  sub _tree_construction_main ($) { Line 4591  sub _tree_construction_main ($) {
4591            !!!cp ('t90');            !!!cp ('t90');
4592            ## As if </noscript>            ## As if </noscript>
4593            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4594            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4595                        
4596            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4597            ## As if </head>            ## As if </head>
# Line 3451  sub _tree_construction_main ($) { Line 4607  sub _tree_construction_main ($) {
4607            !!!cp ('t92');            !!!cp ('t92');
4608          }          }
4609    
4610              ## "after head" insertion mode          ## "after head" insertion mode
4611              ## As if <body>          ## As if <body>
4612              !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
4613              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4614              ## reprocess          ## reprocess
4615              redo B;          next B;
4616            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4617              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4618                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4619                  !!!cp ('t93');              !!!cp ('t93');
4620                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4621                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4622                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4623                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4624                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4625                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4626                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4627                  !!!cp ('t94');              !!!next-token;
4628                  #              next B;
4629                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4630                  !!!cp ('t95');              !!!cp ('t93.2');
4631                  !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4632                  ## Ignore the token                              token => $token);
4633                  !!!next-token;              ## Ignore the token
4634                  redo B;              !!!nack ('t93.3');
4635                }              !!!next-token;
4636              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4637                !!!cp ('t96');            } else {
4638                ## As if <head>              !!!cp ('t95');
4639                !!!create-element ($self->{head_element}, 'head',, $token);              !!!parse-error (type => 'in head:head',
4640                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4641                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4642                !!!nack ('t95.1');
4643                !!!next-token;
4644                next B;
4645              }
4646            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647              !!!cp ('t96');
4648              ## As if <head>
4649              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4650              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4651              push @{$self->{open_elements}},
4652                  [$self->{head_element}, $el_category->{head}];
4653    
4654                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4655                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4656              } else {          } else {
4657                !!!cp ('t97');            !!!cp ('t97');
4658              }          }
4659    
4660              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4661                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4662                  !!!cp ('t98');              !!!cp ('t98');
4663                  ## As if </noscript>              ## As if </noscript>
4664                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4665                  !!!parse-error (type => 'in noscript:base', token => $token);              !!!parse-error (type => 'in noscript', text => 'base',
4666                                              token => $token);
4667                  $self->{insertion_mode} = IN_HEAD_IM;            
4668                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4669                } else {              ## Reprocess in the "in head" insertion mode...
4670                  !!!cp ('t99');            } else {
4671                }              !!!cp ('t99');
4672              }
4673    
4674                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4675                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4676                  !!!cp ('t100');              !!!cp ('t100');
4677                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
4678                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4679                } else {              push @{$self->{open_elements}},
4680                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4681                }              $self->{head_element_inserted} = 1;
4682                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            } else {
4683                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4684                pop @{$self->{open_elements}} # <head>            }
4685                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4686                !!!next-token;            pop @{$self->{open_elements}};
4687                redo B;            pop @{$self->{open_elements}} # <head>
4688              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4689                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4690                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4691                  !!!cp ('t102');            next B;
4692                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);          } elsif ($token->{tag_name} eq 'link') {
4693                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4694                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4695                  !!!cp ('t103');              !!!cp ('t102');
4696                }              !!!parse-error (type => 'after head',
4697                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                              text => $token->{tag_name}, token => $token);
4698                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4699                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4700                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4701                !!!next-token;            } else {
4702                redo B;              !!!cp ('t103');
4703              } elsif ($token->{tag_name} eq 'meta') {            }
4704                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4705                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4706                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4707                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                if $self->{insertion_mode} == AFTER_HEAD_IM;
4708                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4709                } else {            !!!next-token;
4710                  !!!cp ('t105');            next B;
4711                }          } elsif ($token->{tag_name} eq 'command' or
4712                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                   $token->{tag_name} eq 'eventsource') {
4713                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4714                ## NOTE: If the insertion mode at the time of the emission
4715                ## of the token was "before head", $self->{insertion_mode}
4716                ## is already changed to |IN_HEAD_IM|.
4717    
4718                ## NOTE: There is a "as if in head" code clone.
4719                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4720                pop @{$self->{open_elements}};
4721                pop @{$self->{open_elements}} # <head>
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4723                !!!ack ('t103.2');
4724                !!!next-token;
4725                next B;
4726              } else {
4727                ## NOTE: "in head noscript" or "after head" insertion mode
4728                ## - in these cases, these tags are treated as same as
4729                ## normal in-body tags.
4730                !!!cp ('t103.3');
4731                #
4732              }
4733            } elsif ($token->{tag_name} eq 'meta') {
4734              ## NOTE: There is a "as if in head" code clone.
4735              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4736                !!!cp ('t104');
4737                !!!parse-error (type => 'after head',
4738                                text => $token->{tag_name}, token => $token);
4739                push @{$self->{open_elements}},
4740                    [$self->{head_element}, $el_category->{head}];
4741                $self->{head_element_inserted} = 1;
4742              } else {
4743                !!!cp ('t105');
4744              }
4745              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4746              my $meta_el = pop @{$self->{open_elements}};
4747    
4748                unless ($self->{confident}) {                unless ($self->{confident}) {
4749                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4750                    !!!cp ('t106');                    !!!cp ('t106');
4751                      ## NOTE: Whether the encoding is supported or not is handled
4752                      ## in the {change_encoding} callback.
4753                    $self->{change_encoding}                    $self->{change_encoding}
4754                        ->($self, $token->{attributes}->{charset}->{value},                        ->($self, $token->{attributes}->{charset}->{value},
4755                           $token);                           $token);
# Line 3556  sub _tree_construction_main ($) { Line 4759  sub _tree_construction_main ($) {
4759                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4760                                                 ->{has_reference});                                                 ->{has_reference});
4761                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4762                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4763                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4764                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4765                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4766                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4767                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4768                      !!!cp ('t107');                      !!!cp ('t107');
4769                        ## NOTE: Whether the encoding is supported or not is handled
4770                        ## in the {change_encoding} callback.
4771                      $self->{change_encoding}                      $self->{change_encoding}
4772                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4773                             $token);                             $token);
# Line 3593  sub _tree_construction_main ($) { Line 4798  sub _tree_construction_main ($) {
4798    
4799                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4800                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4801                  !!!ack ('t110.1');
4802                !!!next-token;                !!!next-token;
4803                redo B;                next B;
4804              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4805                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4806                  !!!cp ('t111');              !!!cp ('t111');
4807                  ## As if </noscript>              ## As if </noscript>
4808                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4809                  !!!parse-error (type => 'in noscript:title', token => $token);              !!!parse-error (type => 'in noscript', text => 'title',
4810                                              token => $token);
4811                  $self->{insertion_mode} = IN_HEAD_IM;            
4812                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4813                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4814                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4815                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);              !!!cp ('t112');
4816                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4817                } else {                              text => $token->{tag_name}, token => $token);
4818                  !!!cp ('t113');              push @{$self->{open_elements}},
4819                }                  [$self->{head_element}, $el_category->{head}];
4820                $self->{head_element_inserted} = 1;
4821                ## NOTE: There is a "as if in head" code clone.            } else {
4822                my $parent = defined $self->{head_element} ? $self->{head_element}              !!!cp ('t113');
4823                    : $self->{open_elements}->[-1]->[0];            }
4824                $parse_rcdata->(RCDATA_CONTENT_MODEL);  
4825                pop @{$self->{open_elements}} # <head>            ## NOTE: There is a "as if in head" code clone.
4826                    if $self->{insertion_mode} == AFTER_HEAD_IM;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4827                redo B;            pop @{$self->{open_elements}} # <head>
4828              } elsif ($token->{tag_name} eq 'style') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            next B;
4830                ## insertion mode IN_HEAD_IM)          } elsif ($token->{tag_name} eq 'style' or
4831                ## NOTE: There is a "as if in head" code clone.                   $token->{tag_name} eq 'noframes') {
4832                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4833                  !!!cp ('t114');            ## insertion mode IN_HEAD_IM)
4834                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);            ## NOTE: There is a "as if in head" code clone.
4835                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4836                } else {              !!!cp ('t114');
4837                  !!!cp ('t115');              !!!parse-error (type => 'after head',
4838                }                              text => $token->{tag_name}, token => $token);
4839                $parse_rcdata->(CDATA_CONTENT_MODEL);              push @{$self->{open_elements}},
4840                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4841                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4842                redo B;            } else {
4843                !!!cp ('t115');
4844              }
4845              $parse_rcdata->(CDATA_CONTENT_MODEL);
4846              pop @{$self->{open_elements}} # <head>
4847                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4848              next B;
4849              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4850                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4851                  !!!cp ('t116');                  !!!cp ('t116');
4852                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4853                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4854                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4855                    !!!nack ('t116.1');
4856                  !!!next-token;                  !!!next-token;
4857                  redo B;                  next B;
4858                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4859                  !!!cp ('t117');                  !!!cp ('t117');
4860                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4861                                    token => $token);
4862                  ## Ignore the token                  ## Ignore the token
4863                    !!!nack ('t117.1');
4864                  !!!next-token;                  !!!next-token;
4865                  redo B;                  next B;
4866                } else {                } else {
4867                  !!!cp ('t118');                  !!!cp ('t118');
4868                  #                  #
4869                }                }
4870              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4871                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4872                  !!!cp ('t119');              !!!cp ('t119');
4873                  ## As if </noscript>              ## As if </noscript>
4874                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4875                  !!!parse-error (type => 'in noscript:script', token => $token);              !!!parse-error (type => 'in noscript', text => 'script',
4876                                              token => $token);
4877                  $self->{insertion_mode} = IN_HEAD_IM;            
4878                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4879                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4880                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4881                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);              !!!cp ('t120');
4882                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4883                } else {                              text => $token->{tag_name}, token => $token);
4884                  !!!cp ('t121');              push @{$self->{open_elements}},
4885                }                  [$self->{head_element}, $el_category->{head}];
4886                $self->{head_element_inserted} = 1;
4887                ## NOTE: There is a "as if in head" code clone.            } else {
4888                $script_start_tag->();              !!!cp ('t121');
4889                pop @{$self->{open_elements}} # <head>            }
4890                    if $self->{insertion_mode} == AFTER_HEAD_IM;  
4891                redo B;            ## NOTE: There is a "as if in head" code clone.
4892              } elsif ($token->{tag_name} eq 'body' or            $script_start_tag->();
4893                       $token->{tag_name} eq 'frameset') {            pop @{$self->{open_elements}} # <head>
4894                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4895              next B;
4896            } elsif ($token->{tag_name} eq 'body' or
4897                     $token->{tag_name} eq 'frameset') {
4898                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4899                  !!!cp ('t122');                  !!!cp ('t122');
4900                  ## As if </noscript>                  ## As if </noscript>
4901                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4902                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4903                                    text => $token->{tag_name}, token => $token);
4904                                    
4905                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4906                  ## As if </head>                  ## As if </head>
# Line 3707  sub _tree_construction_main ($) { Line 4927  sub _tree_construction_main ($) {
4927                } else {                } else {
4928                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4929                }                }
4930                  !!!nack ('t127.1');
4931                !!!next-token;                !!!next-token;
4932                redo B;                next B;
4933              } else {              } else {
4934                !!!cp ('t128');                !!!cp ('t128');
4935                #                #
# Line 3718  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939                !!!cp ('t129');                !!!cp ('t129');
4940                ## As if </noscript>                ## As if </noscript>
4941                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4942                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4943                                  text => $token->{tag_name}, token => $token);
4944                                
4945                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4946                ## As if </head>                ## As if </head>
# Line 3740  sub _tree_construction_main ($) { Line 4962  sub _tree_construction_main ($) {
4962              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
4963              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4964              ## reprocess              ## reprocess
4965              redo B;              !!!ack-later;
4966                next B;
4967            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4968              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4969                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4970                  !!!cp ('t132');                  !!!cp ('t132');
4971                  ## As if <head>                  ## As if <head>
4972                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4973                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4974                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4975                        [$self->{head_element}, $el_category->{head}];
4976    
4977                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4978                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4979                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4980                  !!!next-token;                  !!!next-token;
4981                  redo B;                  next B;
4982                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4983                  !!!cp ('t133');                  !!!cp ('t133');
4984                  ## As if </noscript>                  ## As if </noscript>
4985                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4986                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4987                                    text => 'head', token => $token);
4988                                    
4989                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4990                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4991                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4992                  !!!next-token;                  !!!next-token;
4993                  redo B;                  next B;
4994                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4995                  !!!cp ('t134');                  !!!cp ('t134');
4996                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4997                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4998                  !!!next-token;                  !!!next-token;
4999                  redo B;                  next B;
5000                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5001                    !!!cp ('t134.1');
5002                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5003                                    token => $token);
5004                    ## Ignore the token
5005                    !!!next-token;
5006                    next B;
5007                } else {                } else {
5008                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
5009                }                }
5010              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5011                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3782  sub _tree_construction_main ($) { Line 5013  sub _tree_construction_main ($) {
5013                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5014                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5015                  !!!next-token;                  !!!next-token;
5016                  redo B;                  next B;
5017                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5018                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5019                  !!!cp ('t137');                  !!!cp ('t137');
5020                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5021                                    text => 'noscript', token => $token);
5022                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5023                  !!!next-token;                  !!!next-token;
5024                  redo B;                  next B;
5025                } else {                } else {
5026                  !!!cp ('t138');                  !!!cp ('t138');
5027                  #                  #
# Line 3796  sub _tree_construction_main ($) { Line 5029  sub _tree_construction_main ($) {
5029              } elsif ({              } elsif ({
5030                        body => 1, html => 1,                        body => 1, html => 1,
5031                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5032                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5033                  !!!cp ('t139');                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5034                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5035                  !!!create-element ($self->{head_element}, 'head',, $token);                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
   
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
5036                  !!!cp ('t140');                  !!!cp ('t140');
5037                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5038                                    text => $token->{tag_name}, token => $token);
5039                    ## Ignore the token
5040                    !!!next-token;
5041                    next B;
5042                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5043                    !!!cp ('t140.1');
5044                    !!!parse-error (type => 'unmatched end tag',
5045                                    text => $token->{tag_name}, token => $token);
5046                  ## Ignore the token                  ## Ignore the token
5047                  !!!next-token;                  !!!next-token;
5048                  redo B;                  next B;
5049                } else {                } else {
5050                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5051                }                }
5052                              } elsif ($token->{tag_name} eq 'p') {
5053                #                !!!cp ('t142');
5054              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5055                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5056                       }->{$token->{tag_name}}) {                ## Ignore the token
5057                  !!!next-token;
5058                  next B;
5059                } elsif ($token->{tag_name} eq 'br') {
5060                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5061                  !!!cp ('t142');                  !!!cp ('t142.2');
5062                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5063                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5064                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5065                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5066      
5067                    ## Reprocess in the "after head" insertion mode...
5068                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5069                    !!!cp ('t143.2');
5070                    ## As if </head>
5071                    pop @{$self->{open_elements}};
5072                    $self->{insertion_mode} = AFTER_HEAD_IM;
5073      
5074                    ## Reprocess in the "after head" insertion mode...
5075                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5076                    !!!cp ('t143.3');
5077                    ## ISSUE: Two parse errors for <head><noscript></br>
5078                    !!!parse-error (type => 'unmatched end tag',
5079                                    text => 'br', token => $token);
5080                    ## As if </noscript>
5081                    pop @{$self->{open_elements}};
5082                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5083    
5084                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5085                } else {                  ## As if </head>
5086                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5087                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5088    
5089                #                  ## Reprocess in the "after head" insertion mode...
5090              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5091                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5092                  #                  #
5093                } else {                } else {
5094                  !!!cp ('t145');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5095                }                }
5096    
5097                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5098                  !!!parse-error (type => 'unmatched end tag',
5099                                  text => 'br', token => $token);
5100                  ## Ignore the token
5101                  !!!next-token;
5102                  next B;
5103                } else {
5104                  !!!cp ('t145');
5105                  !!!parse-error (type => 'unmatched end tag',
5106                                  text => $token->{tag_name}, token => $token);
5107                  ## Ignore the token
5108                  !!!next-token;
5109                  next B;
5110              }              }
5111    
5112              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5113                !!!cp ('t146');                !!!cp ('t146');
5114                ## As if </noscript>                ## As if </noscript>
5115                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5116                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5117                                  text => $token->{tag_name}, token => $token);
5118                                
5119                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5120                ## As if </head>                ## As if </head>
# Line 3866  sub _tree_construction_main ($) { Line 5130  sub _tree_construction_main ($) {
5130              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5131  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5132                !!!cp ('t148');                !!!cp ('t148');
5133                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5134                                  text => $token->{tag_name}, token => $token);
5135                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5136                !!!next-token;                !!!next-token;
5137                redo B;                next B;
5138              } else {              } else {
5139                !!!cp ('t149');                !!!cp ('t149');
5140              }              }
# Line 3879  sub _tree_construction_main ($) { Line 5144  sub _tree_construction_main ($) {
5144              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
5145              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5146              ## reprocess              ## reprocess
5147              redo B;              next B;
5148        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5149          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5150            !!!cp ('t149.1');            !!!cp ('t149.1');
5151    
5152            ## NOTE: As if <head>            ## NOTE: As if <head>
5153            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5154            $self->{open_elements}->[-1]->[0]->append_child            $self->{open_elements}->[-1]->[0]->append_child
5155                ($self->{head_element});                ($self->{head_element});
5156            #push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            #push @{$self->{open_elements}},
5157              #    [$self->{head_element}, $el_category->{head}];
5158            #$self->{insertion_mode} = IN_HEAD_IM;            #$self->{insertion_mode} = IN_HEAD_IM;
5159            ## NOTE: Reprocess.            ## NOTE: Reprocess.
5160    
# Line 3932  sub _tree_construction_main ($) { Line 5198  sub _tree_construction_main ($) {
5198          !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
5199          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5200          ## NOTE: Reprocess.          ## NOTE: Reprocess.
5201          redo B;          next B;
5202        } else {        } else {
5203          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5204        }        }
   
           ## ISSUE: An issue in the spec.  
5205      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5206            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5207              !!!cp ('t150');              !!!cp ('t150');
# Line 3947  sub _tree_construction_main ($) { Line 5211  sub _tree_construction_main ($) {
5211              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5212    
5213              !!!next-token;              !!!next-token;
5214              redo B;              next B;
5215            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5216              if ({              if ({
5217                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3957  sub _tree_construction_main ($) { Line 5221  sub _tree_construction_main ($) {
5221                  ## have an element in table scope                  ## have an element in table scope
5222                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5223                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5224                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5225                      !!!cp ('t151');                      !!!cp ('t151');
5226    
5227                      ## Close the cell                      ## Close the cell
5228                      !!!back-token; # <?>                      !!!back-token; # <x>
5229                      $token = {type => END_TAG_TOKEN, tag_name => $node->[1],                      $token = {type => END_TAG_TOKEN,
5230                                  tag_name => $node->[0]->manakai_local_name,
5231                                line => $token->{line},                                line => $token->{line},
5232                                column => $token->{column}};                                column => $token->{column}};
5233                      redo B;                      next B;
5234                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5235                      !!!cp ('t152');                      !!!cp ('t152');
5236                      ## ISSUE: This case can never be reached, maybe.                      ## ISSUE: This case can never be reached, maybe.
5237                      last;                      last;
# Line 3977  sub _tree_construction_main ($) { Line 5240  sub _tree_construction_main ($) {
5240    
5241                  !!!cp ('t153');                  !!!cp ('t153');
5242                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5243                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5244                  ## Ignore the token                  ## Ignore the token
5245                    !!!nack ('t153.1');
5246                  !!!next-token;                  !!!next-token;
5247                  redo B;                  next B;
5248                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5249                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5250                                    token => $token);
5251                                    
5252                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5253                  ## have a table element in table scope                  ## have a table element in table scope
# Line 3990  sub _tree_construction_main ($) { Line 5255  sub _tree_construction_main ($) {
5255                  INSCOPE: {                  INSCOPE: {
5256                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5257                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5258                      if ($node->[1] eq 'caption') {                      if ($node->[1] & CAPTION_EL) {
5259                        !!!cp ('t155');                        !!!cp ('t155');
5260                        $i = $_;                        $i = $_;
5261                        last INSCOPE;                        last INSCOPE;
5262                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5263                        !!!cp ('t156');                        !!!cp ('t156');
5264                        last;                        last;
5265                      }                      }
# Line 4004  sub _tree_construction_main ($) { Line 5267  sub _tree_construction_main ($) {
5267    
5268                    !!!cp ('t157');                    !!!cp ('t157');
5269                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5270                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5271                    ## Ignore the token                    ## Ignore the token
5272                      !!!nack ('t157.1');
5273                    !!!next-token;                    !!!next-token;
5274                    redo B;                    next B;
5275                  } # INSCOPE                  } # INSCOPE
5276                                    
5277                  ## generate implied end tags                  ## generate implied end tags
5278                  while ({                  while ($self->{open_elements}->[-1]->[1]
5279                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5280                    !!!cp ('t158');                    !!!cp ('t158');
5281                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5282                  }                  }
5283    
5284                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5285                    !!!cp ('t159');                    !!!cp ('t159');
5286                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5287                                      text => $self->{open_elements}->[-1]->[0]
5288                                          ->manakai_local_name,
5289                                      token => $token);
5290                  } else {                  } else {
5291                    !!!cp ('t160');                    !!!cp ('t160');
5292                  }                  }
# Line 4032  sub _tree_construction_main ($) { Line 5298  sub _tree_construction_main ($) {
5298                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5299                                    
5300                  ## reprocess                  ## reprocess
5301                  redo B;                  !!!ack-later;
5302                    next B;
5303                } else {                } else {
5304                  !!!cp ('t161');                  !!!cp ('t161');
5305                  #                  #
# Line 4048  sub _tree_construction_main ($) { Line 5315  sub _tree_construction_main ($) {
5315                  my $i;                  my $i;
5316                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5317                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5318                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5319                      !!!cp ('t163');                      !!!cp ('t163');
5320                      $i = $_;                      $i = $_;
5321                      last INSCOPE;                      last INSCOPE;
5322                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5323                      !!!cp ('t164');                      !!!cp ('t164');
5324                      last INSCOPE;                      last INSCOPE;
5325                    }                    }
5326                  } # INSCOPE                  } # INSCOPE
5327                    unless (defined $i) {                    unless (defined $i) {
5328                      !!!cp ('t165');                      !!!cp ('t165');
5329                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5330                                        text => $token->{tag_name},
5331                                        token => $token);
5332                      ## Ignore the token                      ## Ignore the token
5333                      !!!next-token;                      !!!next-token;
5334                      redo B;                      next B;
5335                    }                    }
5336                                    
5337                  ## generate implied end tags                  ## generate implied end tags
5338                  while ({                  while ($self->{open_elements}->[-1]->[1]
5339                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5340                    !!!cp ('t166');                    !!!cp ('t166');
5341                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5342                  }                  }
5343    
5344                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5345                            ne $token->{tag_name}) {
5346                    !!!cp ('t167');                    !!!cp ('t167');
5347                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5348                                      text => $self->{open_elements}->[-1]->[0]
5349                                          ->manakai_local_name,
5350                                      token => $token);
5351                  } else {                  } else {
5352                    !!!cp ('t168');                    !!!cp ('t168');
5353                  }                  }
# Line 4089  sub _tree_construction_main ($) { Line 5359  sub _tree_construction_main ($) {
5359                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5360                                    
5361                  !!!next-token;                  !!!next-token;
5362                  redo B;                  next B;
5363                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5364                  !!!cp ('t169');                  !!!cp ('t169');
5365                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5366                                    text => $token->{tag_name}, token => $token);
5367                  ## Ignore the token                  ## Ignore the token
5368                  !!!next-token;                  !!!next-token;
5369                  redo B;                  next B;
5370                } else {                } else {
5371                  !!!cp ('t170');                  !!!cp ('t170');
5372                  #                  #
# Line 4107  sub _tree_construction_main ($) { Line 5378  sub _tree_construction_main ($) {
5378                  INSCOPE: {                  INSCOPE: {
5379                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5380                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5381                      if ($node->[1] eq $token->{tag_name}) {                      if ($node->[1] & CAPTION_EL) {
5382                        !!!cp ('t171');                        !!!cp ('t171');
5383                        $i = $_;                        $i = $_;
5384                        last INSCOPE;                        last INSCOPE;
5385                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5386                        !!!cp ('t172');                        !!!cp ('t172');
5387                        last;                        last;
5388                      }                      }
# Line 4121  sub _tree_construction_main ($) { Line 5390  sub _tree_construction_main ($) {
5390    
5391                    !!!cp ('t173');                    !!!cp ('t173');
5392                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5393                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5394                    ## Ignore the token                    ## Ignore the token
5395                    !!!next-token;                    !!!next-token;
5396                    redo B;                    next B;
5397                  } # INSCOPE                  } # INSCOPE
5398                                    
5399                  ## generate implied end tags                  ## generate implied end tags
5400                  while ({                  while ($self->{open_elements}->[-1]->[1]
5401                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5402                    !!!cp ('t174');                    !!!cp ('t174');
5403                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5404                  }                  }
5405                                    
5406                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5407                    !!!cp ('t175');                    !!!cp ('t175');
5408                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5409                                      text => $self->{open_elements}->[-1]->[0]
5410                                          ->manakai_local_name,
5411                                      token => $token);
5412                  } else {                  } else {
5413                    !!!cp ('t176');                    !!!cp ('t176');
5414                  }                  }
# Line 4149  sub _tree_construction_main ($) { Line 5420  sub _tree_construction_main ($) {
5420                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5421                                    
5422                  !!!next-token;                  !!!next-token;
5423                  redo B;                  next B;
5424                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5425                  !!!cp ('t177');                  !!!cp ('t177');
5426                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5427                                    text => $token->{tag_name}, token => $token);
5428                  ## Ignore the token                  ## Ignore the token
5429                  !!!next-token;                  !!!next-token;
5430                  redo B;                  next B;
5431                } else {                } else {
5432                  !!!cp ('t178');                  !!!cp ('t178');
5433                  #                  #
# Line 4171  sub _tree_construction_main ($) { Line 5443  sub _tree_construction_main ($) {
5443                INSCOPE: {                INSCOPE: {
5444                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5445                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5446                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5447                      !!!cp ('t179');                      !!!cp ('t179');
5448                      $i = $_;                      $i = $_;
5449    
5450                      ## Close the cell                      ## Close the cell
5451                      !!!back-token; # </?>                      !!!back-token; # </x>
5452                      $token = {type => END_TAG_TOKEN, tag_name => $tn,                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5453                                line => $token->{line},                                line => $token->{line},
5454                                column => $token->{column}};                                column => $token->{column}};
5455                      redo B;                      next B;
5456                    } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5457                      !!!cp ('t180');                      !!!cp ('t180');
5458                      $tn = $node->[1];                      $tn = $node->[0]->manakai_local_name;
5459                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
5460                      ## in scope in the stack of open elements by definition.                      ## in scope in the stack of open elements by definition.
5461                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5462                      ## ISSUE: Can this be reached?                      ## ISSUE: Can this be reached?
5463                      !!!cp ('t181');                      !!!cp ('t181');
5464                      last;                      last;
# Line 4197  sub _tree_construction_main ($) { Line 5467  sub _tree_construction_main ($) {
5467    
5468                  !!!cp ('t182');                  !!!cp ('t182');
5469                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5470                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5471                  ## Ignore the token                  ## Ignore the token
5472                  !!!next-token;                  !!!next-token;
5473                  redo B;                  next B;
5474                } # INSCOPE                } # INSCOPE
5475              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5476                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5477                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5478                                  token => $token);
5479    
5480                ## As if </caption>                ## As if </caption>
5481                ## have a table element in table scope                ## have a table element in table scope
5482                my $i;                my $i;
5483                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5484                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5485                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5486                    !!!cp ('t184');                    !!!cp ('t184');
5487                    $i = $_;                    $i = $_;
5488                    last INSCOPE;                    last INSCOPE;
5489                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5490                    !!!cp ('t185');                    !!!cp ('t185');
5491                    last INSCOPE;                    last INSCOPE;
5492                  }                  }
5493                } # INSCOPE                } # INSCOPE
5494                unless (defined $i) {                unless (defined $i) {
5495                  !!!cp ('t186');                  !!!cp ('t186');
5496                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5497                                    text => 'caption', token => $token);
5498                  ## Ignore the token                  ## Ignore the token
5499                  !!!next-token;                  !!!next-token;
5500                  redo B;                  next B;
5501                }                }
5502                                
5503                ## generate implied end tags                ## generate implied end tags
5504                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5505                  !!!cp ('t187');                  !!!cp ('t187');
5506                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5507                }                }
5508    
5509                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5510                  !!!cp ('t188');                  !!!cp ('t188');
5511                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5512                                    text => $self->{open_elements}->[-1]->[0]
5513                                        ->manakai_local_name,
5514                                    token => $token);
5515                } else {                } else {
5516                  !!!cp ('t189');                  !!!cp ('t189');
5517                }                }
# Line 4252  sub _tree_construction_main ($) { Line 5523  sub _tree_construction_main ($) {
5523                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5524    
5525                ## reprocess                ## reprocess
5526                redo B;                next B;
5527              } elsif ({              } elsif ({
5528                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5529                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5530                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5531                  !!!cp ('t190');                  !!!cp ('t190');
5532                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5533                                    text => $token->{tag_name}, token => $token);
5534                  ## Ignore the token                  ## Ignore the token
5535                  !!!next-token;                  !!!next-token;
5536                  redo B;                  next B;
5537                } else {                } else {
5538                  !!!cp ('t191');                  !!!cp ('t191');
5539                  #                  #
# Line 4272  sub _tree_construction_main ($) { Line 5544  sub _tree_construction_main ($) {
5544                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5545                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5546                !!!cp ('t192');                !!!cp ('t192');
5547                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5548                                  text => $token->{tag_name}, token => $token);
5549                ## Ignore the token                ## Ignore the token
5550                !!!next-token;                !!!next-token;
5551                redo B;                next B;
5552              } else {              } else {
5553                !!!cp ('t193');                !!!cp ('t193');
5554                #                #
5555              }              }
5556        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5557          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
5558            if (not {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
             dd => 1, dt => 1, li => 1, p => 1, tbody => 1, td => 1, tfoot => 1,  
             th => 1, thead => 1, tr => 1, body => 1, html => 1,  
           }->{$entry->[1]}) {  
5559              !!!cp ('t75');              !!!cp ('t75');
5560              !!!parse-error (type => 'in body:#eof', token => $token);              !!!parse-error (type => 'in body:#eof', token => $token);
5561              last;              last;
# Line 4303  sub _tree_construction_main ($) { Line 5573  sub _tree_construction_main ($) {
5573      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5574        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5575          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5576              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5577            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5578                                
5579            unless (length $token->{data}) {            unless (length $token->{data}) {
5580              !!!cp ('t194');              !!!cp ('t194');
5581              !!!next-token;              !!!next-token;
5582              redo B;              next B;
5583            } else {            } else {
5584              !!!cp ('t195');              !!!cp ('t195');
5585            }            }
5586          }          }
5587    
5588              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5589    
5590              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5591              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5592                            
5593              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5594                   table => 1, tbody => 1, tfoot => 1,            # MUST
5595                   thead => 1, tr => 1,            my $foster_parent_element;
5596                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5597                # MUST            my $prev_sibling;
5598                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5599                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5600                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5601                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5602                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5603                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5604                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5605                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5606                      $foster_parent_element = $parent;                  #
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
5607                } else {                } else {
5608                  !!!cp ('t199');                  !!!cp ('t197');
5609                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5610                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5611                     $next_sibling);                  #
5612                }                }
5613                  last OE;
5614                }
5615              } # OE
5616              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5617              $prev_sibling = $foster_parent_element->last_child
5618                  unless defined $foster_parent_element;
5619              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5620              if (defined $prev_sibling and
5621                  $prev_sibling->node_type == 3) {
5622                !!!cp ('t198');
5623                $prev_sibling->manakai_append_text ($token->{data});
5624              } else {
5625                !!!cp ('t199');
5626                $foster_parent_element->insert_before
5627                    ($self->{document}->create_text_node ($token->{data}),
5628                     $next_sibling);
5629              }
5630            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5631              $open_tables->[-1]->[2] = 1; # ~node inserted
5632          } else {          } else {
5633              ## NOTE: Fragment case or in a foster parent'ed element
5634              ## (e.g. |<table><span>a|).  In fragment case, whether the
5635              ## character is appended to existing node or a new node is
5636              ## created is irrelevant, since the foster parent'ed nodes
5637              ## are discarded and fragment parsing does not invoke any
5638              ## script.
5639            !!!cp ('t200');            !!!cp ('t200');
5640            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5641                  ($token->{data});
5642          }          }
5643                            
5644          !!!next-token;          !!!next-token;
5645          redo B;          next B;
5646        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5647              if ({          if ({
5648                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5649                   th => 1, td => 1,               th => 1, td => 1,
5650                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5651                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5652                  ## Clear back to table context              ## Clear back to table context
5653                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5654                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5655                    !!!cp ('t201');                !!!cp ('t201');
5656                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5657                  }              }
5658                                
5659                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5660                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5661                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5662                }            }
5663              
5664                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5665                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5666                    !!!cp ('t202');                !!!cp ('t202');
5667                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5668                  }              }
5669                                    
5670                  ## Clear back to table body context              ## Clear back to table body context
5671                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5672                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5673                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5674                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5675                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5676                    pop @{$self->{open_elements}};              }
                 }  
5677                                    
5678                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5679                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5680                    !!!cp ('t204');                !!!cp ('t204');
5681                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5682                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5683                    redo B;                !!!nack ('t204');
5684                  } else {                !!!next-token;
5685                    !!!cp ('t205');                next B;
5686                    !!!insert-element ('tr',, $token);              } else {
5687                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5688                  }                !!!insert-element ('tr',, $token);
5689                } else {                ## reprocess in the "in row" insertion mode
5690                  !!!cp ('t206');              }
5691                }            } else {
5692                !!!cp ('t206');
5693              }
5694    
5695                ## Clear back to table row context                ## Clear back to table row context
5696                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5697                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5698                  !!!cp ('t207');                  !!!cp ('t207');
5699                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5700                }                }
5701                                
5702                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5703                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5704              $self->{insertion_mode} = IN_CELL_IM;
5705    
5706                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5707                                
5708              !!!nack ('t207.1');
5709              !!!next-token;
5710              next B;
5711            } elsif ({
5712                      caption => 1, col => 1, colgroup => 1,
5713                      tbody => 1, tfoot => 1, thead => 1,
5714                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5715                     }->{$token->{tag_name}}) {
5716              if ($self->{insertion_mode} == IN_ROW_IM) {
5717                ## As if </tr>
5718                ## have an element in table scope
5719                my $i;
5720                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5721                  my $node = $self->{open_elements}->[$_];
5722                  if ($node->[1] & TABLE_ROW_EL) {
5723                    !!!cp ('t208');
5724                    $i = $_;
5725                    last INSCOPE;
5726                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5727                    !!!cp ('t209');
5728                    last INSCOPE;
5729                  }
5730                } # INSCOPE
5731                unless (defined $i) {
5732                  !!!cp ('t210');
5733                  ## TODO: This type is wrong.
5734                  !!!parse-error (type => 'unmacthed end tag',
5735                                  text => $token->{tag_name}, token => $token);
5736                  ## Ignore the token
5737                  !!!nack ('t210.1');
5738                !!!next-token;                !!!next-token;
5739                redo B;                next B;
5740              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] eq 'tr') {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ({  
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                  !!!cp ('t210');  
 ## TODO: This type is wrong.  
                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!next-token;  
                   redo B;  
                 }  
5741                                    
5742                  ## Clear back to table row context                  ## Clear back to table row context
5743                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5744                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5745                    !!!cp ('t211');                    !!!cp ('t211');
5746                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5747                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4479  sub _tree_construction_main ($) { Line 5752  sub _tree_construction_main ($) {
5752                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5753                    !!!cp ('t212');                    !!!cp ('t212');
5754                    ## reprocess                    ## reprocess
5755                    redo B;                    !!!ack-later;
5756                      next B;
5757                  } else {                  } else {
5758                    !!!cp ('t213');                    !!!cp ('t213');
5759                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4491  sub _tree_construction_main ($) { Line 5765  sub _tree_construction_main ($) {
5765                  my $i;                  my $i;
5766                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5767                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5768                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5769                      !!!cp ('t214');                      !!!cp ('t214');
5770                      $i = $_;                      $i = $_;
5771                      last INSCOPE;                      last INSCOPE;
5772                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5773                      !!!cp ('t215');                      !!!cp ('t215');
5774                      last INSCOPE;                      last INSCOPE;
5775                    }                    }
5776                  } # INSCOPE                  } # INSCOPE
5777                  unless (defined $i) {                  unless (defined $i) {
5778                    !!!cp ('t216');                    !!!cp ('t216');
5779  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5780                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5781                                      text => $token->{tag_name}, token => $token);
5782                    ## Ignore the token                    ## Ignore the token
5783                      !!!nack ('t216.1');
5784                    !!!next-token;                    !!!next-token;
5785                    redo B;                    next B;
5786                  }                  }
5787    
5788                  ## Clear back to table body context                  ## Clear back to table body context
5789                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5790                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5791                    !!!cp ('t217');                    !!!cp ('t217');
5792                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5793                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4536  sub _tree_construction_main ($) { Line 5807  sub _tree_construction_main ($) {
5807                  !!!cp ('t218');                  !!!cp ('t218');
5808                }                }
5809    
5810                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5811                  ## Clear back to table context              ## Clear back to table context
5812                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5813                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5814                    !!!cp ('t219');                !!!cp ('t219');
5815                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5816                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5817                  }              }
5818                                
5819                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
5820                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5821                  ## reprocess              ## reprocess
5822                  redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5823                } elsif ({              !!!ack-later;
5824                          caption => 1,              next B;
5825                          colgroup => 1,            } elsif ({
5826                          tbody => 1, tfoot => 1, thead => 1,                      caption => 1,
5827                         }->{$token->{tag_name}}) {                      colgroup => 1,
5828                  ## Clear back to table context                      tbody => 1, tfoot => 1, thead => 1,
5829                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                     }->{$token->{tag_name}}) {
5830                         $self->{open_elements}->[-1]->[1] ne 'html') {              ## Clear back to table context
5831                    while (not ($self->{open_elements}->[-1]->[1]
5832                                    & TABLE_SCOPING_EL)) {
5833                    !!!cp ('t220');                    !!!cp ('t220');
5834                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5835                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5836                  }                  }
5837                                    
5838                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5839                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5840                                    
5841                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5842                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5843                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5844                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5845                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5846                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5847                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5848                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5849                  !!!next-token;                                        }->{$token->{tag_name}};
5850                  redo B;              !!!next-token;
5851                } else {              !!!nack ('t220.1');
5852                  die "$0: in table: <>: $token->{tag_name}";              next B;
5853                }            } else {
5854                die "$0: in table: <>: $token->{tag_name}";
5855              }
5856              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5857                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
5858                                  text => $self->{open_elements}->[-1]->[0]
5859                                      ->manakai_local_name,
5860                                  token => $token);
5861    
5862                ## As if </table>                ## As if </table>
5863                ## have a table element in table scope                ## have a table element in table scope
5864                my $i;                my $i;
5865                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5866                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5867                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5868                    !!!cp ('t221');                    !!!cp ('t221');
5869                    $i = $_;                    $i = $_;
5870                    last INSCOPE;                    last INSCOPE;
5871                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5872                    !!!cp ('t222');                    !!!cp ('t222');
5873                    last INSCOPE;                    last INSCOPE;
5874                  }                  }
# Line 4601  sub _tree_construction_main ($) { Line 5876  sub _tree_construction_main ($) {
5876                unless (defined $i) {                unless (defined $i) {
5877                  !!!cp ('t223');                  !!!cp ('t223');
5878  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5879                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5880                                    token => $token);
5881                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5882                    !!!nack ('t223.1');
5883                  !!!next-token;                  !!!next-token;
5884                  redo B;                  next B;
5885                }                }
5886                                
5887  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5888                ## generate implied end tags                ## generate implied end tags
5889                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5890                  !!!cp ('t224');                  !!!cp ('t224');
5891                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5892                }                }
5893    
5894                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5895                  !!!cp ('t225');                  !!!cp ('t225');
5896  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5897                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5898                                    text => $self->{open_elements}->[-1]->[0]
5899                                        ->manakai_local_name,
5900                                    token => $token);
5901                } else {                } else {
5902                  !!!cp ('t226');                  !!!cp ('t226');
5903                }                }
# Line 4629  sub _tree_construction_main ($) { Line 5907  sub _tree_construction_main ($) {
5907    
5908                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5909    
5910                ## reprocess            ## reprocess
5911                redo B;            !!!ack-later;
5912              next B;
5913          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5914            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5915              !!!cp ('t227.8');              !!!cp ('t227.8');
5916              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5917              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5918              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5919                next B;
5920            } else {            } else {
5921              !!!cp ('t227.7');              !!!cp ('t227.7');
5922              #              #
# Line 4646  sub _tree_construction_main ($) { Line 5926  sub _tree_construction_main ($) {
5926              !!!cp ('t227.6');              !!!cp ('t227.6');
5927              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5928              $script_start_tag->();              $script_start_tag->();
5929              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5930                next B;
5931            } else {            } else {
5932              !!!cp ('t227.5');              !!!cp ('t227.5');
5933              #              #
# Line 4657  sub _tree_construction_main ($) { Line 5938  sub _tree_construction_main ($) {
5938                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5939                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5940                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5941                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5942                                    text => $token->{tag_name}, token => $token);
5943    
5944                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5945                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5946    
5947                  ## TODO: form element pointer                  ## TODO: form element pointer
5948    
5949                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5950    
5951                  !!!next-token;                  !!!next-token;
5952                  redo B;                  !!!ack ('t227.2.1');
5953                    next B;
5954                } else {                } else {
5955                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5956                  #                  #
# Line 4684  sub _tree_construction_main ($) { Line 5968  sub _tree_construction_main ($) {
5968            #            #
5969          }          }
5970    
5971          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5972                            token => $token);
5973    
5974          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5975          #          #
# Line 4695  sub _tree_construction_main ($) { Line 5980  sub _tree_construction_main ($) {
5980                my $i;                my $i;
5981                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5982                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5983                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5984                    !!!cp ('t228');                    !!!cp ('t228');
5985                    $i = $_;                    $i = $_;
5986                    last INSCOPE;                    last INSCOPE;
5987                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5988                    !!!cp ('t229');                    !!!cp ('t229');
5989                    last INSCOPE;                    last INSCOPE;
5990                  }                  }
5991                } # INSCOPE                } # INSCOPE
5992                unless (defined $i) {                unless (defined $i) {
5993                  !!!cp ('t230');                  !!!cp ('t230');
5994                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5995                                    text => $token->{tag_name}, token => $token);
5996                  ## Ignore the token                  ## Ignore the token
5997                    !!!nack ('t230.1');
5998                  !!!next-token;                  !!!next-token;
5999                  redo B;                  next B;
6000                } else {                } else {
6001                  !!!cp ('t232');                  !!!cp ('t232');
6002                }                }
6003    
6004                ## Clear back to table row context                ## Clear back to table row context
6005                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6006                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6007                  !!!cp ('t231');                  !!!cp ('t231');
6008  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6009                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4728  sub _tree_construction_main ($) { Line 6012  sub _tree_construction_main ($) {
6012                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6013                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6014                !!!next-token;                !!!next-token;
6015                redo B;                !!!nack ('t231.1');
6016                  next B;
6017              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6018                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6019                  ## As if </tr>                  ## As if </tr>
# Line 4736  sub _tree_construction_main ($) { Line 6021  sub _tree_construction_main ($) {
6021                  my $i;                  my $i;
6022                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6023                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6024                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6025                      !!!cp ('t233');                      !!!cp ('t233');
6026                      $i = $_;                      $i = $_;
6027                      last INSCOPE;                      last INSCOPE;
6028                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6029                      !!!cp ('t234');                      !!!cp ('t234');
6030                      last INSCOPE;                      last INSCOPE;
6031                    }                    }
# Line 4750  sub _tree_construction_main ($) { Line 6033  sub _tree_construction_main ($) {
6033                  unless (defined $i) {                  unless (defined $i) {
6034                    !!!cp ('t235');                    !!!cp ('t235');
6035  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6036                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6037                                      text => $token->{type}, token => $token);
6038                    ## Ignore the token                    ## Ignore the token
6039                      !!!nack ('t236.1');
6040                    !!!next-token;                    !!!next-token;
6041                    redo B;                    next B;
6042                  }                  }
6043                                    
6044                  ## Clear back to table row context                  ## Clear back to table row context
6045                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6046                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6047                    !!!cp ('t236');                    !!!cp ('t236');
6048  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6049                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4775  sub _tree_construction_main ($) { Line 6059  sub _tree_construction_main ($) {
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 ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6063                      !!!cp ('t237');                      !!!cp ('t237');
6064                      $i = $_;                      $i = $_;
6065                      last INSCOPE;                      last INSCOPE;
6066                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6067                      !!!cp ('t238');                      !!!cp ('t238');
6068                      last INSCOPE;                      last INSCOPE;
6069                    }                    }
6070                  } # INSCOPE                  } # INSCOPE
6071                  unless (defined $i) {                  unless (defined $i) {
6072                    !!!cp ('t239');                    !!!cp ('t239');
6073                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6074                                      text => $token->{tag_name}, token => $token);
6075                    ## Ignore the token                    ## Ignore the token
6076                      !!!nack ('t239.1');
6077                    !!!next-token;                    !!!next-token;
6078                    redo B;                    next B;
6079                  }                  }
6080                                    
6081                  ## Clear back to table body context                  ## Clear back to table body context
6082                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6083                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6084                    !!!cp ('t240');                    !!!cp ('t240');
6085                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6086                  }                  }
# Line 4825  sub _tree_construction_main ($) { Line 6106  sub _tree_construction_main ($) {
6106                my $i;                my $i;
6107                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6108                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6109                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6110                    !!!cp ('t241');                    !!!cp ('t241');
6111                    $i = $_;                    $i = $_;
6112                    last INSCOPE;                    last INSCOPE;
6113                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6114                    !!!cp ('t242');                    !!!cp ('t242');
6115                    last INSCOPE;                    last INSCOPE;
6116                  }                  }
6117                } # INSCOPE                } # INSCOPE
6118                unless (defined $i) {                unless (defined $i) {
6119                  !!!cp ('t243');                  !!!cp ('t243');
6120                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6121                                    text => $token->{tag_name}, token => $token);
6122                  ## Ignore the token                  ## Ignore the token
6123                    !!!nack ('t243.1');
6124                  !!!next-token;                  !!!next-token;
6125                  redo B;                  next B;
6126                }                }
6127                                    
6128                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4850  sub _tree_construction_main ($) { Line 6131  sub _tree_construction_main ($) {
6131                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6132                                
6133                !!!next-token;                !!!next-token;
6134                redo B;                next B;
6135              } elsif ({              } elsif ({
6136                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6137                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4860  sub _tree_construction_main ($) { Line 6141  sub _tree_construction_main ($) {
6141                  my $i;                  my $i;
6142                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6143                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6144                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6145                      !!!cp ('t247');                      !!!cp ('t247');
6146                      $i = $_;                      $i = $_;
6147                      last INSCOPE;                      last INSCOPE;
6148                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6149                      !!!cp ('t248');                      !!!cp ('t248');
6150                      last INSCOPE;                      last INSCOPE;
6151                    }                    }
6152                  } # INSCOPE                  } # INSCOPE
6153                    unless (defined $i) {                    unless (defined $i) {
6154                      !!!cp ('t249');                      !!!cp ('t249');
6155                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6156                                        text => $token->{tag_name}, token => $token);
6157                      ## Ignore the token                      ## Ignore the token
6158                        !!!nack ('t249.1');
6159                      !!!next-token;                      !!!next-token;
6160                      redo B;                      next B;
6161                    }                    }
6162                                    
6163                  ## As if </tr>                  ## As if </tr>
# Line 4884  sub _tree_construction_main ($) { Line 6165  sub _tree_construction_main ($) {
6165                  my $i;                  my $i;
6166                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6167                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6168                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6169                      !!!cp ('t250');                      !!!cp ('t250');
6170                      $i = $_;                      $i = $_;
6171                      last INSCOPE;                      last INSCOPE;
6172                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6173                      !!!cp ('t251');                      !!!cp ('t251');
6174                      last INSCOPE;                      last INSCOPE;
6175                    }                    }
6176                  } # INSCOPE                  } # INSCOPE
6177                    unless (defined $i) {                    unless (defined $i) {
6178                      !!!cp ('t252');                      !!!cp ('t252');
6179                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6180                                        text => 'tr', token => $token);
6181                      ## Ignore the token                      ## Ignore the token
6182                        !!!nack ('t252.1');
6183                      !!!next-token;                      !!!next-token;
6184                      redo B;                      next B;
6185                    }                    }
6186                                    
6187                  ## Clear back to table row context                  ## Clear back to table row context
6188                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6189                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6190                    !!!cp ('t253');                    !!!cp ('t253');
6191  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6192                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4921  sub _tree_construction_main ($) { Line 6201  sub _tree_construction_main ($) {
6201                my $i;                my $i;
6202                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6203                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6204                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6205                    !!!cp ('t254');                    !!!cp ('t254');
6206                    $i = $_;                    $i = $_;
6207                    last INSCOPE;                    last INSCOPE;
6208                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6209                    !!!cp ('t255');                    !!!cp ('t255');
6210                    last INSCOPE;                    last INSCOPE;
6211                  }                  }
6212                } # INSCOPE                } # INSCOPE
6213                unless (defined $i) {                unless (defined $i) {
6214                  !!!cp ('t256');                  !!!cp ('t256');
6215                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6216                                    text => $token->{tag_name}, token => $token);
6217                  ## Ignore the token                  ## Ignore the token
6218                    !!!nack ('t256.1');
6219                  !!!next-token;                  !!!next-token;
6220                  redo B;                  next B;
6221                }                }
6222    
6223                ## Clear back to table body context                ## Clear back to table body context
6224                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6225                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6226                  !!!cp ('t257');                  !!!cp ('t257');
6227  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6228                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4951  sub _tree_construction_main ($) { Line 6230  sub _tree_construction_main ($) {
6230    
6231                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6232                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6233                  !!!nack ('t257.1');
6234                !!!next-token;                !!!next-token;
6235                redo B;                next B;
6236              } elsif ({              } elsif ({
6237                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6238                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6239                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6240                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6241                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6242                !!!cp ('t258');            !!!cp ('t258');
6243                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6244                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6245                !!!next-token;            ## Ignore the token
6246                redo B;            !!!nack ('t258.1');
6247               !!!next-token;
6248              next B;
6249          } else {          } else {
6250            !!!cp ('t259');            !!!cp ('t259');
6251            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6252                              text => $token->{tag_name}, token => $token);
6253    
6254            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6255            #            #
6256          }          }
6257        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6258          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6259                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6260            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
6261            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 4989  sub _tree_construction_main ($) { Line 6272  sub _tree_construction_main ($) {
6272        }        }
6273      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6274            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6275              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6276                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6277                unless (length $token->{data}) {                unless (length $token->{data}) {
6278                  !!!cp ('t260');                  !!!cp ('t260');
6279                  !!!next-token;                  !!!next-token;
6280                  redo B;                  next B;
6281                }                }
6282              }              }
6283                            
# Line 5005  sub _tree_construction_main ($) { Line 6288  sub _tree_construction_main ($) {
6288                !!!cp ('t262');                !!!cp ('t262');
6289                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6290                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6291                  !!!ack ('t262.1');
6292                !!!next-token;                !!!next-token;
6293                redo B;                next B;
6294              } else {              } else {
6295                !!!cp ('t263');                !!!cp ('t263');
6296                #                #
6297              }              }
6298            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6299              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6300                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6301                  !!!cp ('t264');                  !!!cp ('t264');
6302                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6303                                    text => 'colgroup', token => $token);
6304                  ## Ignore the token                  ## Ignore the token
6305                  !!!next-token;                  !!!next-token;
6306                  redo B;                  next B;
6307                } else {                } else {
6308                  !!!cp ('t265');                  !!!cp ('t265');
6309                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6310                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6311                  !!!next-token;                  !!!next-token;
6312                  redo B;                              next B;            
6313                }                }
6314              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6315                !!!cp ('t266');                !!!cp ('t266');
6316                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6317                                  text => 'col', token => $token);
6318                ## Ignore the token                ## Ignore the token
6319                !!!next-token;                !!!next-token;
6320                redo B;                next B;
6321              } else {              } else {
6322                !!!cp ('t267');                !!!cp ('t267');
6323                #                #
6324              }              }
6325        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6326          if ($self->{open_elements}->[-1]->[1] eq 'html' or          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6327              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
6328            !!!cp ('t270.2');            !!!cp ('t270.2');
6329            ## Stop parsing.            ## Stop parsing.
# Line 5048  sub _tree_construction_main ($) { Line 6334  sub _tree_construction_main ($) {
6334            pop @{$self->{open_elements}}; # colgroup            pop @{$self->{open_elements}}; # colgroup
6335            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6336            ## Reprocess.            ## Reprocess.
6337            redo B;            next B;
6338          }          }
6339        } else {        } else {
6340          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6341        }        }
6342    
6343            ## As if </colgroup>            ## As if </colgroup>
6344            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6345              !!!cp ('t269');              !!!cp ('t269');
6346  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6347              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6348                                text => 'colgroup', token => $token);
6349              ## Ignore the token              ## Ignore the token
6350                !!!nack ('t269.1');
6351              !!!next-token;              !!!next-token;
6352              redo B;              next B;
6353            } else {            } else {
6354              !!!cp ('t270');              !!!cp ('t270');
6355              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6356              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6357                !!!ack-later;
6358              ## reprocess              ## reprocess
6359              redo B;              next B;
6360            }            }
6361      } elsif ($self->{insertion_mode} & SELECT_IMS) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6362        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6363          !!!cp ('t271');          !!!cp ('t271');
6364          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6365          !!!next-token;          !!!next-token;
6366          redo B;          next B;
6367        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6368              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6369                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6370                  !!!cp ('t272');              !!!cp ('t272');
6371                  ## As if </option>              ## As if </option>
6372                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6373                } else {            } else {
6374                  !!!cp ('t273');              !!!cp ('t273');
6375                }            }
6376    
6377                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6378                !!!next-token;            !!!nack ('t273.1');
6379                redo B;            !!!next-token;
6380              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6381                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6382                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6383                  ## As if </option>              !!!cp ('t274');
6384                  pop @{$self->{open_elements}};              ## As if </option>
6385                } else {              pop @{$self->{open_elements}};
6386                  !!!cp ('t275');            } else {
6387                }              !!!cp ('t275');
6388              }
6389    
6390                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6391                  !!!cp ('t276');              !!!cp ('t276');
6392                  ## As if </optgroup>              ## As if </optgroup>
6393                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6394                } else {            } else {
6395                  !!!cp ('t277');              !!!cp ('t277');
6396                }            }
6397    
6398                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6399                !!!next-token;            !!!nack ('t277.1');
6400                redo B;            !!!next-token;
6401          } elsif ($token->{tag_name} eq 'select' or            next B;
6402                   $token->{tag_name} eq 'input' or          } elsif ({
6403                       select => 1, input => 1, textarea => 1,
6404                     }->{$token->{tag_name}} or
6405                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6406                    {                    {
6407                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5117  sub _tree_construction_main ($) { Line 6409  sub _tree_construction_main ($) {
6409                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6410                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6411            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6412            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6413                              token => $token);
6414            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6415            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6416                ## have an element in table scope            ## have an element in table scope
6417                my $i;            my $i;
6418                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6419                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6420                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6421                    !!!cp ('t278');                !!!cp ('t278');
6422                    $i = $_;                $i = $_;
6423                    last INSCOPE;                last INSCOPE;
6424                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6425                            table => 1, html => 1,                !!!cp ('t279');
6426                           }->{$node->[1]}) {                last INSCOPE;
6427                    !!!cp ('t279');              }
6428                    last INSCOPE;            } # INSCOPE
6429                  }            unless (defined $i) {
6430                } # INSCOPE              !!!cp ('t280');
6431                unless (defined $i) {              !!!parse-error (type => 'unmatched end tag',
6432                  !!!cp ('t280');                              text => 'select', token => $token);
6433                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              ## Ignore the token
6434                  ## Ignore the token              !!!nack ('t280.1');
6435                  !!!next-token;              !!!next-token;
6436                  redo B;              next B;
6437                }            }
6438                                
6439                !!!cp ('t281');            !!!cp ('t281');
6440                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6441    
6442                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6443    
6444            if ($token->{tag_name} eq 'select') {            if ($token->{tag_name} eq 'select') {
6445              !!!cp ('t281.2');              !!!nack ('t281.2');
6446              !!!next-token;              !!!next-token;
6447              redo B;              next B;
6448            } else {            } else {
6449              !!!cp ('t281.1');              !!!cp ('t281.1');
6450                !!!ack-later;
6451              ## Reprocess the token.              ## Reprocess the token.
6452              redo B;              next B;
6453            }            }
6454          } else {          } else {
6455            !!!cp ('t282');            !!!cp ('t282');
6456            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6457                              text => $token->{tag_name}, token => $token);
6458            ## Ignore the token            ## Ignore the token
6459              !!!nack ('t282.1');
6460            !!!next-token;            !!!next-token;
6461            redo B;            next B;
6462          }          }
6463        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6464              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6465                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6466                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6467                  !!!cp ('t283');              !!!cp ('t283');
6468                  ## As if </option>              ## As if </option>
6469                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6470                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6471                  !!!cp ('t284');              !!!cp ('t284');
6472                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6473                } else {            } else {
6474                  !!!cp ('t285');              !!!cp ('t285');
6475                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6476                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6477                }              ## Ignore the token
6478                !!!next-token;            }
6479                redo B;            !!!nack ('t285.1');
6480              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6481                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6482                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6483                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6484                } else {              !!!cp ('t286');
6485                  !!!cp ('t287');              pop @{$self->{open_elements}};
6486                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            } else {
6487                  ## Ignore the token              !!!cp ('t287');
6488                }              !!!parse-error (type => 'unmatched end tag',
6489                !!!next-token;                              text => $token->{tag_name}, token => $token);
6490                redo B;              ## Ignore the token
6491              } elsif ($token->{tag_name} eq 'select') {            }
6492                ## have an element in table scope            !!!nack ('t287.1');
6493                my $i;            !!!next-token;
6494                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6495                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6496                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6497                    !!!cp ('t288');            my $i;
6498                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6499                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6500                  } elsif ({              if ($node->[1] & SELECT_EL) {
6501                            table => 1, html => 1,                !!!cp ('t288');
6502                           }->{$node->[1]}) {                $i = $_;
6503                    !!!cp ('t289');                last INSCOPE;
6504                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6505                  }                !!!cp ('t289');
6506                } # INSCOPE                last INSCOPE;
6507                unless (defined $i) {              }
6508                  !!!cp ('t290');            } # INSCOPE
6509                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            unless (defined $i) {
6510                  ## Ignore the token              !!!cp ('t290');
6511                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6512                  redo B;                              text => $token->{tag_name}, token => $token);
6513                }              ## Ignore the token
6514                !!!nack ('t290.1');
6515                !!!next-token;
6516                next B;
6517              }
6518                                
6519                !!!cp ('t291');            !!!cp ('t291');
6520                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6521    
6522                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6523    
6524                !!!next-token;            !!!nack ('t291.1');
6525                redo B;            !!!next-token;
6526              next B;
6527          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6528                   {                   {
6529                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
6530                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6531                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6532  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6533                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6534                              text => $token->{tag_name}, token => $token);
6535                                
6536                ## have an element in table scope            ## have an element in table scope
6537                my $i;            my $i;
6538                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6539                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6540                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6541                    !!!cp ('t292');                !!!cp ('t292');
6542                    $i = $_;                $i = $_;
6543                    last INSCOPE;                last INSCOPE;
6544                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6545                            table => 1, html => 1,                !!!cp ('t293');
6546                           }->{$node->[1]}) {                last INSCOPE;
6547                    !!!cp ('t293');              }
6548                    last INSCOPE;            } # INSCOPE
6549                  }            unless (defined $i) {
6550                } # INSCOPE              !!!cp ('t294');
6551                unless (defined $i) {              ## Ignore the token
6552                  !!!cp ('t294');              !!!nack ('t294.1');
6553                  ## Ignore the token              !!!next-token;
6554                  !!!next-token;              next B;
6555                  redo B;            }
               }  
6556                                
6557                ## As if </select>            ## As if </select>
6558                ## have an element in table scope            ## have an element in table scope
6559                undef $i;            undef $i;
6560                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6561                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6562                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6563                    !!!cp ('t295');                !!!cp ('t295');
6564                    $i = $_;                $i = $_;
6565                    last INSCOPE;                last INSCOPE;
6566                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6567  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6568                    !!!cp ('t296');                !!!cp ('t296');
6569                    last INSCOPE;                last INSCOPE;
6570                  }              }
6571                } # INSCOPE            } # INSCOPE
6572                unless (defined $i) {            unless (defined $i) {
6573                  !!!cp ('t297');              !!!cp ('t297');
6574  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6575                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6576                  ## Ignore the </select> token                              text => 'select', token => $token);
6577                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6578                  redo B;              !!!nack ('t297.1');
6579                }              !!!next-token; ## TODO: ok?
6580                next B;
6581              }
6582                                
6583                !!!cp ('t298');            !!!cp ('t298');
6584                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6585    
6586                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6587    
6588                ## reprocess            !!!ack-later;
6589                redo B;            ## reprocess
6590              next B;
6591          } else {          } else {
6592            !!!cp ('t299');            !!!cp ('t299');
6593            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6594                              text => $token->{tag_name}, token => $token);
6595            ## Ignore the token            ## Ignore the token
6596              !!!nack ('t299.3');
6597            !!!next-token;            !!!next-token;
6598            redo B;            next B;
6599          }          }
6600        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6601          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6602                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6603            !!!cp ('t299.1');            !!!cp ('t299.1');
6604            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5309  sub _tree_construction_main ($) { Line 6613  sub _tree_construction_main ($) {
6613        }        }
6614      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6615        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6616          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6617            my $data = $1;            my $data = $1;
6618            ## As if in body            ## As if in body
6619            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5319  sub _tree_construction_main ($) { Line 6623  sub _tree_construction_main ($) {
6623            unless (length $token->{data}) {            unless (length $token->{data}) {
6624              !!!cp ('t300');              !!!cp ('t300');
6625              !!!next-token;              !!!next-token;
6626              redo B;              next B;
6627            }            }
6628          }          }
6629                    
6630          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6631            !!!cp ('t301');            !!!cp ('t301');
6632            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6633              #
           ## Reprocess in the "after body" insertion mode.  
6634          } else {          } else {
6635            !!!cp ('t302');            !!!cp ('t302');
6636              ## "after body" insertion mode
6637              !!!parse-error (type => 'after body:#text', token => $token);
6638              #
6639          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6640    
6641          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6642          ## reprocess          ## reprocess
6643          redo B;          next B;
6644        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6645          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6646            !!!cp ('t303');            !!!cp ('t303');
6647            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6648                                        text => $token->{tag_name}, token => $token);
6649            ## Reprocess in the "after body" insertion mode.            #
6650          } else {          } else {
6651            !!!cp ('t304');            !!!cp ('t304');
6652              ## "after body" insertion mode
6653              !!!parse-error (type => 'after body',
6654                              text => $token->{tag_name}, token => $token);
6655              #
6656          }          }
6657    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6658          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6659            !!!ack-later;
6660          ## reprocess          ## reprocess
6661          redo B;          next B;
6662        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6663          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6664            !!!cp ('t305');            !!!cp ('t305');
6665            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6666                              text => $token->{tag_name}, token => $token);
6667                        
6668            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6669            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6670              next B;
6671          } else {          } else {
6672            !!!cp ('t306');            !!!cp ('t306');
6673          }          }
# Line 5369  sub _tree_construction_main ($) { Line 6676  sub _tree_construction_main ($) {
6676          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6677            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6678              !!!cp ('t307');              !!!cp ('t307');
6679              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6680                                text => 'html', token => $token);
6681              ## Ignore the token              ## Ignore the token
6682              !!!next-token;              !!!next-token;
6683              redo B;              next B;
6684            } else {            } else {
6685              !!!cp ('t308');              !!!cp ('t308');
6686              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6687              !!!next-token;              !!!next-token;
6688              redo B;              next B;
6689            }            }
6690          } else {          } else {
6691            !!!cp ('t309');            !!!cp ('t309');
6692            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6693                              text => $token->{tag_name}, token => $token);
6694    
6695            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6696            ## reprocess            ## reprocess
6697            redo B;            next B;
6698          }          }
6699        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6700          !!!cp ('t309.2');          !!!cp ('t309.2');
# Line 5396  sub _tree_construction_main ($) { Line 6705  sub _tree_construction_main ($) {
6705        }        }
6706      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6707        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6708          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6709            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6710                        
6711            unless (length $token->{data}) {            unless (length $token->{data}) {
6712              !!!cp ('t310');              !!!cp ('t310');
6713              !!!next-token;              !!!next-token;
6714              redo B;              next B;
6715            }            }
6716          }          }
6717                    
6718          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6719            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6720              !!!cp ('t311');              !!!cp ('t311');
6721              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6722            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6723              !!!cp ('t312');              !!!cp ('t312');
6724              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6725            } else { # "after html frameset"            } else { # "after after frameset"
6726              !!!cp ('t313');              !!!cp ('t313');
6727              !!!parse-error (type => 'after html:#character', token => $token);              !!!parse-error (type => 'after html:#text', token => $token);
   
             $self->{insertion_mode} = AFTER_FRAMESET_IM;  
             ## Reprocess in the "after frameset" insertion mode.  
             !!!parse-error (type => 'after frameset:#character', token => $token);  
6728            }            }
6729                        
6730            ## Ignore the token.            ## Ignore the token.
# Line 5430  sub _tree_construction_main ($) { Line 6735  sub _tree_construction_main ($) {
6735              !!!cp ('t315');              !!!cp ('t315');
6736              !!!next-token;              !!!next-token;
6737            }            }
6738            redo B;            next B;
6739          }          }
6740                    
6741          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6742        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t316');  
           !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
6743          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6744              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6745            !!!cp ('t318');            !!!cp ('t318');
6746            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6747              !!!nack ('t318.1');
6748            !!!next-token;            !!!next-token;
6749            redo B;            next B;
6750          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6751                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6752            !!!cp ('t319');            !!!cp ('t319');
6753            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6754            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6755              !!!ack ('t319.1');
6756            !!!next-token;            !!!next-token;
6757            redo B;            next B;
6758          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6759            !!!cp ('t320');            !!!cp ('t320');
6760            ## NOTE: As if in body.            ## NOTE: As if in head.
6761            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6762            redo B;            next B;
6763    
6764              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6765              ## has no parse error.
6766          } else {          } else {
6767            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6768              !!!cp ('t321');              !!!cp ('t321');
6769              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6770            } else {                              text => $token->{tag_name}, token => $token);
6771              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6772              !!!cp ('t322');              !!!cp ('t322');
6773              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6774                                text => $token->{tag_name}, token => $token);
6775              } else { # "after after frameset"
6776                !!!cp ('t322.2');
6777                !!!parse-error (type => 'after after frameset',
6778                                text => $token->{tag_name}, token => $token);
6779            }            }
6780            ## Ignore the token            ## Ignore the token
6781              !!!nack ('t322.1');
6782            !!!next-token;            !!!next-token;
6783            redo B;            next B;
6784          }          }
6785        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!cp ('t323');  
           !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
6786          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6787              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6788            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6789                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6790              !!!cp ('t325');              !!!cp ('t325');
6791              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6792                                text => $token->{tag_name}, token => $token);
6793              ## Ignore the token              ## Ignore the token
6794              !!!next-token;              !!!next-token;
6795            } else {            } else {
# Line 5501  sub _tree_construction_main ($) { Line 6799  sub _tree_construction_main ($) {
6799            }            }
6800    
6801            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6802                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6803              !!!cp ('t327');              !!!cp ('t327');
6804              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6805            } else {            } else {
6806              !!!cp ('t328');              !!!cp ('t328');
6807            }            }
6808            redo B;            next B;
6809          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6810                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6811            !!!cp ('t329');            !!!cp ('t329');
6812            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6813            !!!next-token;            !!!next-token;
6814            redo B;            next B;
6815          } else {          } else {
6816            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6817              !!!cp ('t330');              !!!cp ('t330');
6818              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6819            } else {                              text => $token->{tag_name}, token => $token);
6820              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6821                !!!cp ('t330.1');
6822                !!!parse-error (type => 'after frameset:/',
6823                                text => $token->{tag_name}, token => $token);
6824              } else { # "after after html"
6825              !!!cp ('t331');              !!!cp ('t331');
6826              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6827                                text => $token->{tag_name}, token => $token);
6828            }            }
6829            ## Ignore the token            ## Ignore the token
6830            !!!next-token;            !!!next-token;
6831            redo B;            next B;
6832          }          }
6833        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6834          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6835                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6836            !!!cp ('t331.1');            !!!cp ('t331.1');
6837            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5540  sub _tree_construction_main ($) { Line 6844  sub _tree_construction_main ($) {
6844        } else {        } else {
6845          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6846        }        }
   
       ## ISSUE: An issue in spec here  
6847      } else {      } else {
6848        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6849      }      }
# Line 5552  sub _tree_construction_main ($) { Line 6854  sub _tree_construction_main ($) {
6854          !!!cp ('t332');          !!!cp ('t332');
6855          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6856          $script_start_tag->();          $script_start_tag->();
6857          redo B;          next B;
6858        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6859          !!!cp ('t333');          !!!cp ('t333');
6860          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6861          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6862          redo B;          next B;
6863        } elsif ({        } elsif ({
6864                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6865                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6866          !!!cp ('t334');          !!!cp ('t334');
6867          ## 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
6868          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6869          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6870            !!!ack ('t334.1');
6871          !!!next-token;          !!!next-token;
6872          redo B;          next B;
6873        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6874          ## 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
6875          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6876          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6877    
6878          unless ($self->{confident}) {          unless ($self->{confident}) {
6879            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6880              !!!cp ('t335');              !!!cp ('t335');
6881                ## NOTE: Whether the encoding is supported or not is handled
6882                ## in the {change_encoding} callback.
6883              $self->{change_encoding}              $self->{change_encoding}
6884                  ->($self, $token->{attributes}->{charset}->{value}, $token);                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6885                            
# Line 5583  sub _tree_construction_main ($) { Line 6888  sub _tree_construction_main ($) {
6888                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6889                                           ->{has_reference});                                           ->{has_reference});
6890            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6891              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6892                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6893                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6894                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6895                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6896                       /x) {
6897                !!!cp ('t336');                !!!cp ('t336');
6898                  ## NOTE: Whether the encoding is supported or not is handled
6899                  ## in the {change_encoding} callback.
6900                $self->{change_encoding}                $self->{change_encoding}
6901                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6902                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
# Line 5615  sub _tree_construction_main ($) { Line 6922  sub _tree_construction_main ($) {
6922            }            }
6923          }          }
6924    
6925            !!!ack ('t338.1');
6926          !!!next-token;          !!!next-token;
6927          redo B;          next B;
6928        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6929          !!!cp ('t341');          !!!cp ('t341');
6930          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6931          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6932          redo B;          next B;
6933        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6934          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6935                                
6936          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6937              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6938            !!!cp ('t342');            !!!cp ('t342');
6939            ## Ignore the token            ## Ignore the token
6940          } else {          } else {
# Line 5640  sub _tree_construction_main ($) { Line 6948  sub _tree_construction_main ($) {
6948              }              }
6949            }            }
6950          }          }
6951            !!!nack ('t343.1');
6952          !!!next-token;          !!!next-token;
6953          redo B;          next B;
6954        } elsif ({        } elsif ({
6955                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6956                  div => 1, dl => 1, fieldset => 1,  
6957                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6958                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6959                    center => 1, datagrid => 1, details => 1, dialog => 1,
6960                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6961                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6962                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6963                    section => 1, ul => 1,
6964                    ## NOTE: As normal, but drops leading newline
6965                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6966                    ## NOTE: As normal, but interacts with the form element pointer
6967                  form => 1,                  form => 1,
6968                    
6969                  table => 1,                  table => 1,
6970                  hr => 1,                  hr => 1,
6971                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 5656  sub _tree_construction_main ($) { Line 6973  sub _tree_construction_main ($) {
6973            !!!cp ('t350');            !!!cp ('t350');
6974            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
6975            ## Ignore the token            ## Ignore the token
6976              !!!nack ('t350.1');
6977            !!!next-token;            !!!next-token;
6978            redo B;            next B;
6979          }          }
6980    
6981          ## has a p element in scope          ## has a p element in scope
6982          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6983            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6984              !!!cp ('t344');              !!!cp ('t344');
6985              !!!back-token;              !!!back-token; # <form>
6986              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6987                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
6988              redo B;              next B;
6989            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6990              !!!cp ('t345');              !!!cp ('t345');
6991              last INSCOPE;              last INSCOPE;
6992            }            }
# Line 5679  sub _tree_construction_main ($) { Line 6994  sub _tree_construction_main ($) {
6994                        
6995          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6996          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6997              !!!nack ('t346.1');
6998            !!!next-token;            !!!next-token;
6999            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7000              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5695  sub _tree_construction_main ($) { Line 7011  sub _tree_construction_main ($) {
7011            !!!cp ('t347.1');            !!!cp ('t347.1');
7012            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
7013    
7014              !!!nack ('t347.2');
7015            !!!next-token;            !!!next-token;
7016          } elsif ($token->{tag_name} eq 'table') {          } elsif ($token->{tag_name} eq 'table') {
7017            !!!cp ('t382');            !!!cp ('t382');
# Line 5702  sub _tree_construction_main ($) { Line 7019  sub _tree_construction_main ($) {
7019                        
7020            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
7021    
7022              !!!nack ('t382.1');
7023            !!!next-token;            !!!next-token;
7024          } elsif ($token->{tag_name} eq 'hr') {          } elsif ($token->{tag_name} eq 'hr') {
7025            !!!cp ('t386');            !!!cp ('t386');
7026            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7027                    
7028              !!!nack ('t386.1');
7029            !!!next-token;            !!!next-token;
7030          } else {          } else {
7031            !!!cp ('t347');            !!!nack ('t347.1');
7032            !!!next-token;            !!!next-token;
7033          }          }
7034          redo B;          next B;
7035        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
7036          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7037    
7038            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7039              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7040              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7041              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7042              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7043              ## object (Fx)
7044              ## Generate non-tree (non-conforming)
7045              ## basefont (IE7 (where basefont is non-void)), center (IE),
7046              ## form (IE), hn (IE)
7047            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7048              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7049              ## div (Fx, S)
7050    
7051            my $non_optional;
7052            my $i = -1;
7053    
7054            ## 1.
7055            for my $node (reverse @{$self->{open_elements}}) {
7056              if ($node->[1] & LI_EL) {
7057                ## 2. (a) As if </li>
7058                {
7059                  ## If no </li> - not applied
7060                  #
7061    
7062                  ## Otherwise
7063    
7064                  ## 1. generate implied end tags, except for </li>
7065                  #
7066    
7067                  ## 2. If current node != "li", parse error
7068                  if ($non_optional) {
7069                    !!!parse-error (type => 'not closed',
7070                                    text => $non_optional->[0]->manakai_local_name,
7071                                    token => $token);
7072                    !!!cp ('t355');
7073                  } else {
7074                    !!!cp ('t356');
7075                  }
7076    
7077                  ## 3. Pop
7078                  splice @{$self->{open_elements}}, $i;
7079                }
7080    
7081                last; ## 2. (b) goto 5.
7082              } elsif (
7083                       ## NOTE: not "formatting" and not "phrasing"
7084                       ($node->[1] & SPECIAL_EL or
7085                        $node->[1] & SCOPING_EL) and
7086                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7087    
7088                       (not $node->[1] & ADDRESS_EL) &
7089                       (not $node->[1] & DIV_EL) &
7090                       (not $node->[1] & P_EL)) {
7091                ## 3.
7092                !!!cp ('t357');
7093                last; ## goto 5.
7094              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7095                !!!cp ('t358');
7096                #
7097              } else {
7098                !!!cp ('t359');
7099                $non_optional ||= $node;
7100                #
7101              }
7102              ## 4.
7103              ## goto 2.
7104              $i--;
7105            }
7106    
7107            ## 5. (a) has a |p| element in scope
7108          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7109            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7110              !!!cp ('t353');              !!!cp ('t353');
7111              !!!back-token;  
7112                ## NOTE: |<p><li>|, for example.
7113    
7114                !!!back-token; # <x>
7115              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7116                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7117              redo B;              next B;
7118            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7119              !!!cp ('t354');              !!!cp ('t354');
7120              last INSCOPE;              last INSCOPE;
7121            }            }
7122          } # INSCOPE          } # INSCOPE
7123              
7124          ## Step 1          ## 5. (b) insert
7125            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7126            !!!nack ('t359.1');
7127            !!!next-token;
7128            next B;
7129          } elsif ($token->{tag_name} eq 'dt' or
7130                   $token->{tag_name} eq 'dd') {
7131            ## NOTE: As normal, but imply </dt> or </dd> when ...
7132    
7133            my $non_optional;
7134          my $i = -1;          my $i = -1;
7135          my $node = $self->{open_elements}->[$i];  
7136          my $li_or_dtdd = {li => {li => 1},          ## 1.
7137                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
7138                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7139          LI: {              ## 2. (a) As if </li>
7140            ## Step 2              {
7141            if ($li_or_dtdd->{$node->[1]}) {                ## If no </li> - not applied
7142              if ($i != -1) {                #
7143                !!!cp ('t355');  
7144                !!!parse-error (type => 'end tag missing:'.                ## Otherwise
7145                                $self->{open_elements}->[-1]->[1], token => $token);  
7146              } else {                ## 1. generate implied end tags, except for </dt> or </dd>
7147                !!!cp ('t356');                #
7148    
7149                  ## 2. If current node != "dt"|"dd", parse error
7150                  if ($non_optional) {
7151                    !!!parse-error (type => 'not closed',
7152                                    text => $non_optional->[0]->manakai_local_name,
7153                                    token => $token);
7154                    !!!cp ('t355.1');
7155                  } else {
7156                    !!!cp ('t356.1');
7157                  }
7158    
7159                  ## 3. Pop
7160                  splice @{$self->{open_elements}}, $i;
7161              }              }
7162              splice @{$self->{open_elements}}, $i;  
7163              last LI;              last; ## 2. (b) goto 5.
7164              } elsif (
7165                       ## NOTE: not "formatting" and not "phrasing"
7166                       ($node->[1] & SPECIAL_EL or
7167                        $node->[1] & SCOPING_EL) and
7168                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7169    
7170                       (not $node->[1] & ADDRESS_EL) &
7171                       (not $node->[1] & DIV_EL) &
7172                       (not $node->[1] & P_EL)) {
7173                ## 3.
7174                !!!cp ('t357.1');
7175                last; ## goto 5.
7176              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7177                !!!cp ('t358.1');
7178                #
7179            } else {            } else {
7180              !!!cp ('t357');              !!!cp ('t359.1');
7181            }              $non_optional ||= $node;
7182                          #
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             !!!cp ('t358');  
             last LI;  
7183            }            }
7184                        ## 4.
7185            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7186            $i--;            $i--;
7187            $node = $self->{open_elements}->[$i];          }
7188            redo LI;  
7189          } # LI          ## 5. (a) has a |p| element in scope
7190                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7191              if ($_->[1] & P_EL) {
7192                !!!cp ('t353.1');
7193                !!!back-token; # <x>
7194                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7195                          line => $token->{line}, column => $token->{column}};
7196                next B;
7197              } elsif ($_->[1] & SCOPING_EL) {
7198                !!!cp ('t354.1');
7199                last INSCOPE;
7200              }
7201            } # INSCOPE
7202    
7203            ## 5. (b) insert
7204          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7205            !!!nack ('t359.2');
7206          !!!next-token;          !!!next-token;
7207          redo B;          next B;
7208        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7209            ## NOTE: As normal, but effectively ends parsing
7210    
7211          ## has a p element in scope          ## has a p element in scope
7212          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7213            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7214              !!!cp ('t367');              !!!cp ('t367');
7215              !!!back-token;              !!!back-token; # <plaintext>
7216              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7217                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7218              redo B;              next B;
7219            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7220              !!!cp ('t368');              !!!cp ('t368');
7221              last INSCOPE;              last INSCOPE;
7222            }            }
# Line 5795  sub _tree_construction_main ($) { Line 7226  sub _tree_construction_main ($) {
7226                        
7227          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7228                        
7229            !!!nack ('t368.1');
7230          !!!next-token;          !!!next-token;
7231          redo B;          next B;
7232        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7233          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7234            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7235            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7236              !!!cp ('t371');              !!!cp ('t371');
7237              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
7238                            
7239              !!!back-token;              !!!back-token; # <a>
7240              $token = {type => END_TAG_TOKEN, tag_name => 'a',              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7241                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7242              $formatting_end_tag->($token);              $formatting_end_tag->($token);
# Line 5835  sub _tree_construction_main ($) { Line 7267  sub _tree_construction_main ($) {
7267          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7268          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7269    
7270            !!!nack ('t374.1');
7271          !!!next-token;          !!!next-token;
7272          redo B;          next B;
7273        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7274          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7275    
7276          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7277          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7278            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7279            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7280              !!!cp ('t376');              !!!cp ('t376');
7281              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
7282              !!!back-token;              !!!back-token; # <nobr>
7283              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7284                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7285              redo B;              next B;
7286            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7287              !!!cp ('t377');              !!!cp ('t377');
7288              last INSCOPE;              last INSCOPE;
7289            }            }
# Line 5862  sub _tree_construction_main ($) { Line 7292  sub _tree_construction_main ($) {
7292          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7293          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7294                    
7295            !!!nack ('t377.1');
7296          !!!next-token;          !!!next-token;
7297          redo B;          next B;
7298        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7299          ## has a button element in scope          ## has a button element in scope
7300          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7301            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7302            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7303              !!!cp ('t378');              !!!cp ('t378');
7304              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
7305              !!!back-token;              !!!back-token; # <button>
7306              $token = {type => END_TAG_TOKEN, tag_name => 'button',              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7307                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7308              redo B;              next B;
7309            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7310              !!!cp ('t379');              !!!cp ('t379');
7311              last INSCOPE;              last INSCOPE;
7312            }            }
# Line 5892  sub _tree_construction_main ($) { Line 7320  sub _tree_construction_main ($) {
7320    
7321          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7322    
7323            !!!nack ('t379.1');
7324          !!!next-token;          !!!next-token;
7325          redo B;          next B;
7326        } elsif ({        } elsif ({
7327                  xmp => 1,                  xmp => 1,
7328                  iframe => 1,                  iframe => 1,
7329                  noembed => 1,                  noembed => 1,
7330                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7331                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7332                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7333          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 5909  sub _tree_construction_main ($) { Line 7338  sub _tree_construction_main ($) {
7338          }          }
7339          ## NOTE: There is an "as if in body" code clone.          ## NOTE: There is an "as if in body" code clone.
7340          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
7341          redo B;          next B;
7342        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7343          !!!parse-error (type => 'isindex', token => $token);          !!!parse-error (type => 'isindex', token => $token);
7344                    
7345          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7346            !!!cp ('t389');            !!!cp ('t389');
7347            ## Ignore the token            ## Ignore the token
7348              !!!nack ('t389'); ## NOTE: Not acknowledged.
7349            !!!next-token;            !!!next-token;
7350            redo B;            next B;
7351          } else {          } else {
7352              !!!ack ('t391.1');
7353    
7354            my $at = $token->{attributes};            my $at = $token->{attributes};
7355            my $form_attrs;            my $form_attrs;
7356            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5962  sub _tree_construction_main ($) { Line 7394  sub _tree_construction_main ($) {
7394                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7395                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7396                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           $token = shift @tokens;  
7397            !!!back-token (@tokens);            !!!back-token (@tokens);
7398            redo B;            !!!next-token;
7399              next B;
7400          }          }
7401        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7402          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7403          my $el;          my $el;
7404          !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7405                    
7406          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7407          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5978  sub _tree_construction_main ($) { Line 7410  sub _tree_construction_main ($) {
7410          $insert->($el);          $insert->($el);
7411                    
7412          my $text = '';          my $text = '';
7413            !!!nack ('t392.1');
7414          !!!next-token;          !!!next-token;
7415          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7416            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 6008  sub _tree_construction_main ($) { Line 7441  sub _tree_construction_main ($) {
7441            ## Ignore the token            ## Ignore the token
7442          } else {          } else {
7443            !!!cp ('t398');            !!!cp ('t398');
7444            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7445          }          }
7446          !!!next-token;          !!!next-token;
7447            next B;
7448          } elsif ($token->{tag_name} eq 'optgroup' or
7449                   $token->{tag_name} eq 'option') {
7450            ## has an |option| element in scope
7451            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7452              my $node = $self->{open_elements}->[$_];
7453              if ($node->[1] & OPTION_EL) {
7454                !!!cp ('t397.1');
7455                ## NOTE: As if </option>
7456                !!!back-token; # <option> or <optgroup>
7457                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7458                          line => $token->{line}, column => $token->{column}};
7459                next B;
7460              } elsif ($node->[1] & SCOPING_EL) {
7461                !!!cp ('t397.2');
7462                last INSCOPE;
7463              }
7464            } # INSCOPE
7465    
7466            $reconstruct_active_formatting_elements->($insert_to_current);
7467    
7468            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7469    
7470            !!!nack ('t397.3');
7471            !!!next-token;
7472            redo B;
7473          } elsif ($token->{tag_name} eq 'rt' or
7474                   $token->{tag_name} eq 'rp') {
7475            ## has a |ruby| element in scope
7476            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7477              my $node = $self->{open_elements}->[$_];
7478              if ($node->[1] & RUBY_EL) {
7479                !!!cp ('t398.1');
7480                ## generate implied end tags
7481                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7482                  !!!cp ('t398.2');
7483                  pop @{$self->{open_elements}};
7484                }
7485                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7486                  !!!cp ('t398.3');
7487                  !!!parse-error (type => 'not closed',
7488                                  text => $self->{open_elements}->[-1]->[0]
7489                                      ->manakai_local_name,
7490                                  token => $token);
7491                  pop @{$self->{open_elements}}
7492                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7493                }
7494                last INSCOPE;
7495              } elsif ($node->[1] & SCOPING_EL) {
7496                !!!cp ('t398.4');
7497                last INSCOPE;
7498              }
7499            } # INSCOPE
7500    
7501            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7502    
7503            !!!nack ('t398.5');
7504            !!!next-token;
7505          redo B;          redo B;
7506          } elsif ($token->{tag_name} eq 'math' or
7507                   $token->{tag_name} eq 'svg') {
7508            $reconstruct_active_formatting_elements->($insert_to_current);
7509    
7510            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7511    
7512            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7513    
7514            ## "adjust foreign attributes" - done in insert-element-f
7515            
7516            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7517            
7518            if ($self->{self_closing}) {
7519              pop @{$self->{open_elements}};
7520              !!!ack ('t398.6');
7521            } else {
7522              !!!cp ('t398.7');
7523              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7524              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7525              ## mode, "in body" (not "in foreign content") secondary insertion
7526              ## mode, maybe.
7527            }
7528    
7529            !!!next-token;
7530            next B;
7531        } elsif ({        } elsif ({
7532                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7533                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7534                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7535                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7536                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7537          !!!cp ('t401');          !!!cp ('t401');
7538          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7539                            text => $token->{tag_name}, token => $token);
7540          ## Ignore the token          ## Ignore the token
7541            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7542            !!!next-token;
7543            next B;
7544          } elsif ($token->{tag_name} eq 'param' or
7545                   $token->{tag_name} eq 'source') {
7546            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7547            pop @{$self->{open_elements}};
7548    
7549            !!!ack ('t398.5');
7550          !!!next-token;          !!!next-token;
7551          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7552        } else {        } else {
7553          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
7554            !!!cp ('t384');            !!!cp ('t384');
# Line 6044  sub _tree_construction_main ($) { Line 7568  sub _tree_construction_main ($) {
7568              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
7569            !!!cp ('t380');            !!!cp ('t380');
7570            push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
7571              !!!nack ('t380.1');
7572          } elsif ({          } elsif ({
7573                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7574                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7575                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7576                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7577            !!!cp ('t375');            !!!cp ('t375');
7578            push @$active_formatting_elements, $self->{open_elements}->[-1];            push @$active_formatting_elements, $self->{open_elements}->[-1];
7579              !!!nack ('t375.1');
7580          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
7581            !!!cp ('t388');            !!!cp ('t388');
7582            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7583            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7584              !!!ack ('t388.2');
7585          } elsif ({          } elsif ({
7586                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7587                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
7588                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7589            !!!cp ('t388.1');            !!!cp ('t388.1');
7590            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7591              !!!ack ('t388.3');
7592          } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select') {
7593            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7594                    
# Line 6074  sub _tree_construction_main ($) { Line 7601  sub _tree_construction_main ($) {
7601              !!!cp ('t400.2');              !!!cp ('t400.2');
7602              $self->{insertion_mode} = IN_SELECT_IM;              $self->{insertion_mode} = IN_SELECT_IM;
7603            }            }
7604              !!!nack ('t400.3');
7605          } else {          } else {
7606            !!!cp ('t402');            !!!nack ('t402');
7607          }          }
7608                    
7609          !!!next-token;          !!!next-token;
7610          redo B;          next B;
7611        }        }
7612      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7613        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
# Line 6087  sub _tree_construction_main ($) { Line 7615  sub _tree_construction_main ($) {
7615          my $i;          my $i;
7616          INSCOPE: {          INSCOPE: {
7617            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
7618              if ($_->[1] eq 'body') {              if ($_->[1] & BODY_EL) {
7619                !!!cp ('t405');                !!!cp ('t405');
7620                $i = $_;                $i = $_;
7621                last INSCOPE;                last INSCOPE;
7622              } elsif ({              } elsif ($_->[1] & SCOPING_EL) {
                       applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
7623                !!!cp ('t405.1');                !!!cp ('t405.1');
7624                last;                last;
7625              }              }
7626            }            }
7627    
7628            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7629                            value => $token->{tag_name}, token => $token);  
7630              !!!parse-error (type => 'unmatched end tag',
7631                              text => $token->{tag_name}, token => $token);
7632            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7633            !!!next-token;            !!!next-token;
7634            redo B;            next B;
7635          } # INSCOPE          } # INSCOPE
7636    
7637          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
7638            unless ({            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
                    dd => 1, dt => 1, li => 1, p => 1, td => 1,  
                    th => 1, tr => 1, body => 1, html => 1,  
                    tbody => 1, tfoot => 1, thead => 1,  
                   }->{$_->[1]}) {  
7639              !!!cp ('t403');              !!!cp ('t403');
7640              !!!parse-error (type => 'not closed:'.$_->[1], token => $token);              !!!parse-error (type => 'not closed',
7641                                text => $_->[0]->manakai_local_name,
7642                                token => $token);
7643              last;              last;
7644            } else {            } else {
7645              !!!cp ('t404');              !!!cp ('t404');
# Line 6123  sub _tree_construction_main ($) { Line 7648  sub _tree_construction_main ($) {
7648    
7649          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
7650          !!!next-token;          !!!next-token;
7651          redo B;          next B;
7652        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7653          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
7654            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7655            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7656                $self->{open_elements}->[1]->[1] & BODY_EL) {
7657              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7658              !!!cp ('t406');              !!!cp ('t406');
7659              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7660                                text => $self->{open_elements}->[1]->[0]
7661                                    ->manakai_local_name,
7662                                token => $token);
7663            } else {            } else {
7664              !!!cp ('t407');              !!!cp ('t407');
7665            }            }
7666            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7667            ## reprocess            ## reprocess
7668            redo B;            next B;
7669          } else {          } else {
7670            !!!cp ('t408');            !!!cp ('t408');
7671            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7672                              text => $token->{tag_name}, token => $token);
7673            ## Ignore the token            ## Ignore the token
7674            !!!next-token;            !!!next-token;
7675            redo B;            next B;
7676          }          }
7677        } elsif ({        } elsif ({
7678                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7679                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7680                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7681                    address => 1, article => 1, aside => 1, blockquote => 1,
7682                    center => 1, datagrid => 1, details => 1, dialog => 1,
7683                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7684                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7685                    ol => 1, pre => 1, section => 1, ul => 1,
7686    
7687                    ## NOTE: As normal, but ... optional tags
7688                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7689    
7690                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7691                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7692            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7693            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7694            ## </dd>" code.
7695    
7696          ## has an element in scope          ## has an element in scope
7697          my $i;          my $i;
7698          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7699            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7700            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7701              !!!cp ('t410');              !!!cp ('t410');
7702              $i = $_;              $i = $_;
7703              last INSCOPE;              last INSCOPE;
7704            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7705              !!!cp ('t411');              !!!cp ('t411');
7706              last INSCOPE;              last INSCOPE;
7707            }            }
# Line 6169  sub _tree_construction_main ($) { Line 7709  sub _tree_construction_main ($) {
7709    
7710          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7711            !!!cp ('t413');            !!!cp ('t413');
7712            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7713                              text => $token->{tag_name}, token => $token);
7714              ## NOTE: Ignore the token.
7715          } else {          } else {
7716            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7717            while ({            while ({
7718                      ## END_TAG_OPTIONAL_EL
7719                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7720                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7721                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7722                      option => 1,
7723                      optgroup => 1,
7724                    p => 1,                    p => 1,
7725                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7726                      rp => 1,
7727                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7728              !!!cp ('t409');              !!!cp ('t409');
7729              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7730            }            }
7731    
7732            ## Step 2.            ## Step 2.
7733            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7734                      ne $token->{tag_name}) {
7735              !!!cp ('t412');              !!!cp ('t412');
7736              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7737                                text => $self->{open_elements}->[-1]->[0]
7738                                    ->manakai_local_name,
7739                                token => $token);
7740            } else {            } else {
7741              !!!cp ('t414');              !!!cp ('t414');
7742            }            }
# Line 6200  sub _tree_construction_main ($) { Line 7751  sub _tree_construction_main ($) {
7751                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7752          }          }
7753          !!!next-token;          !!!next-token;
7754          redo B;          next B;
7755        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7756            ## NOTE: As normal, but interacts with the form element pointer
7757    
7758          undef $self->{form_element};          undef $self->{form_element};
7759    
7760          ## has an element in scope          ## has an element in scope
7761          my $i;          my $i;
7762          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7763            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7764            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7765              !!!cp ('t418');              !!!cp ('t418');
7766              $i = $_;              $i = $_;
7767              last INSCOPE;              last INSCOPE;
7768            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7769              !!!cp ('t419');              !!!cp ('t419');
7770              last INSCOPE;              last INSCOPE;
7771            }            }
# Line 6223  sub _tree_construction_main ($) { Line 7773  sub _tree_construction_main ($) {
7773    
7774          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7775            !!!cp ('t421');            !!!cp ('t421');
7776            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7777                              text => $token->{tag_name}, token => $token);
7778              ## NOTE: Ignore the token.
7779          } else {          } else {
7780            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7781            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7782              !!!cp ('t417');              !!!cp ('t417');
7783              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7784            }            }
7785                        
7786            ## Step 2.            ## Step 2.
7787            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7788                      ne $token->{tag_name}) {
7789              !!!cp ('t417.1');              !!!cp ('t417.1');
7790              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7791                                text => $self->{open_elements}->[-1]->[0]
7792                                    ->manakai_local_name,
7793                                token => $token);
7794            } else {            } else {
7795              !!!cp ('t420');              !!!cp ('t420');
7796            }              }  
# Line 6246  sub _tree_construction_main ($) { Line 7800  sub _tree_construction_main ($) {
7800          }          }
7801    
7802          !!!next-token;          !!!next-token;
7803          redo B;          next B;
7804        } elsif ({        } elsif ({
7805                    ## NOTE: As normal, except acts as a closer for any ...
7806                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7807                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7808          ## has an element in scope          ## has an element in scope
7809          my $i;          my $i;
7810          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7811            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7812            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7813              !!!cp ('t423');              !!!cp ('t423');
7814              $i = $_;              $i = $_;
7815              last INSCOPE;              last INSCOPE;
7816            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7817              !!!cp ('t424');              !!!cp ('t424');
7818              last INSCOPE;              last INSCOPE;
7819            }            }
# Line 6271  sub _tree_construction_main ($) { Line 7821  sub _tree_construction_main ($) {
7821    
7822          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7823            !!!cp ('t425.1');            !!!cp ('t425.1');
7824            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7825                              text => $token->{tag_name}, token => $token);
7826              ## NOTE: Ignore the token.
7827          } else {          } else {
7828            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7829            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7830              !!!cp ('t422');              !!!cp ('t422');
7831              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7832            }            }
7833                        
7834            ## Step 2.            ## Step 2.
7835            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7836                      ne $token->{tag_name}) {
7837              !!!cp ('t425');              !!!cp ('t425');
7838              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7839                                text => $token->{tag_name}, token => $token);
7840            } else {            } else {
7841              !!!cp ('t426');              !!!cp ('t426');
7842            }            }
# Line 6294  sub _tree_construction_main ($) { Line 7846  sub _tree_construction_main ($) {
7846          }          }
7847                    
7848          !!!next-token;          !!!next-token;
7849          redo B;          next B;
7850        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7851            ## NOTE: As normal, except </p> implies <p> and ...
7852    
7853          ## has an element in scope          ## has an element in scope
7854            my $non_optional;
7855          my $i;          my $i;
7856          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7857            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7858            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7859              !!!cp ('t410.1');              !!!cp ('t410.1');
7860              $i = $_;              $i = $_;
7861              last INSCOPE;              last INSCOPE;
7862            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7863              !!!cp ('t411.1');              !!!cp ('t411.1');
7864              last INSCOPE;              last INSCOPE;
7865              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7866                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7867                !!!cp ('t411.2');
7868                #
7869              } else {
7870                !!!cp ('t411.3');
7871                $non_optional ||= $node;
7872                #
7873            }            }
7874          } # INSCOPE          } # INSCOPE
7875    
7876          if (defined $i) {          if (defined $i) {
7877            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7878              #
7879    
7880              ## 2. If current node != "p", parse error
7881              if ($non_optional) {
7882              !!!cp ('t412.1');              !!!cp ('t412.1');
7883              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7884                                text => $non_optional->[0]->manakai_local_name,
7885                                token => $token);
7886            } else {            } else {
7887              !!!cp ('t414.1');              !!!cp ('t414.1');
7888            }            }
7889    
7890              ## 3. Pop
7891            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7892          } else {          } else {
7893            !!!cp ('t413.1');            !!!cp ('t413.1');
7894            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7895                              text => $token->{tag_name}, token => $token);
7896    
7897            !!!cp ('t415.1');            !!!cp ('t415.1');
7898            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7899            my $el;            my $el;
7900            !!!create-element ($el, 'p',, $token);            !!!create-element ($el, $HTML_NS, 'p',, $token);
7901            $insert->($el);            $insert->($el);
7902            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7903          }          }
7904    
7905          !!!next-token;          !!!next-token;
7906          redo B;          next B;
7907        } elsif ({        } elsif ({
7908                  a => 1,                  a => 1,
7909                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7910                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7911                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7912                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7913          !!!cp ('t427');          !!!cp ('t427');
7914          $formatting_end_tag->($token);          $formatting_end_tag->($token);
7915          redo B;          next B;
7916        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7917          !!!cp ('t428');          !!!cp ('t428');
7918          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7919                            text => 'br', token => $token);
7920    
7921          ## As if <br>          ## As if <br>
7922          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7923                    
7924          my $el;          my $el;
7925          !!!create-element ($el, 'br',, $token);          !!!create-element ($el, $HTML_NS, 'br',, $token);
7926          $insert->($el);          $insert->($el);
7927                    
7928          ## Ignore the token.          ## Ignore the token.
7929          !!!next-token;          !!!next-token;
7930          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}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7931        } else {        } else {
7932            if ($token->{tag_name} eq 'sarcasm') {
7933              sleep 0.001; # take a deep breath
7934            }
7935    
7936          ## Step 1          ## Step 1
7937          my $node_i = -1;          my $node_i = -1;
7938          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7939    
7940          ## Step 2          ## Step 2
7941          S2: {          S2: {
7942            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7943              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7944              if ($node_tag_name eq $token->{tag_name}) {
7945              ## Step 1              ## Step 1
7946              ## generate implied end tags              ## generate implied end tags
7947              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7948                !!!cp ('t430');                !!!cp ('t430');
7949                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7950                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7951                  ## which seems wrong.
7952                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7953                  $node_i++;
7954              }              }
7955                    
7956              ## Step 2              ## Step 2
7957              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7958                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7959                $current_tag_name =~ tr/A-Z/a-z/;
7960                if ($current_tag_name ne $token->{tag_name}) {
7961                !!!cp ('t431');                !!!cp ('t431');
7962                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7963                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
7964                                  text => $self->{open_elements}->[-1]->[0]
7965                                      ->manakai_local_name,
7966                                  token => $token);
7967              } else {              } else {
7968                !!!cp ('t432');                !!!cp ('t432');
7969              }              }
7970                            
7971              ## Step 3              ## Step 3
7972              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7973    
7974              !!!next-token;              !!!next-token;
7975              last S2;              last S2;
7976            } else {            } else {
7977              ## Step 3              ## Step 3
7978              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7979                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7980                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7981                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7982                !!!cp ('t433');                !!!cp ('t433');
7983                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7984                                  text => $token->{tag_name}, token => $token);
7985                ## Ignore the token                ## Ignore the token
7986                !!!next-token;                !!!next-token;
7987                last S2;                last S2;
             }  
7988    
7989                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7990                  ## 9.27, "a" is a child of <dd> (conforming).  In
7991                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7992                  ## "a" is a child of both <body> and <dd>.
7993                }
7994                
7995              !!!cp ('t434');              !!!cp ('t434');
7996            }            }
7997                        
# Line 6434  sub _tree_construction_main ($) { Line 8002  sub _tree_construction_main ($) {
8002            ## Step 5;            ## Step 5;
8003            redo S2;            redo S2;
8004          } # S2          } # S2
8005          redo B;          next B;
8006        }        }
8007      }      }
8008      redo B;      next B;
8009      } continue { # B
8010        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8011          ## NOTE: The code below is executed in cases where it does not have
8012          ## to be, but it it is harmless even in those cases.
8013          ## has an element in scope
8014          INSCOPE: {
8015            for (reverse 0..$#{$self->{open_elements}}) {
8016              my $node = $self->{open_elements}->[$_];
8017              if ($node->[1] & FOREIGN_EL) {
8018                last INSCOPE;
8019              } elsif ($node->[1] & SCOPING_EL) {
8020                last;
8021              }
8022            }
8023            
8024            ## NOTE: No foreign element in scope.
8025            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8026          } # INSCOPE
8027        }
8028    } # B    } # B
8029    
8030    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6445  sub _tree_construction_main ($) { Line 8032  sub _tree_construction_main ($) {
8032    ## TODO: script stuffs    ## TODO: script stuffs
8033  } # _tree_construct_main  } # _tree_construct_main
8034    
8035  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8036    my $class = shift;    my $class = shift;
8037    my $node = shift;    my $node = shift;
8038    my $s = \$_[0];    #my $s = \$_[0];
8039    my $onerror = $_[1];    my $onerror = $_[1];
8040      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8041    
8042    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8043    
# Line 6468  sub set_inner_html ($$$) { Line 8056  sub set_inner_html ($$$) {
8056      }      }
8057    
8058      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8059      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8060    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8061      ## TODO: If non-html element      ## TODO: If non-html element
8062    
8063      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8064    
8065    ## TODO: Support for $get_wrapper
8066    
8067      ## Step 1 # MUST      ## Step 1 # MUST
8068      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8069      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6483  sub set_inner_html ($$$) { Line 8073  sub set_inner_html ($$$) {
8073    
8074      ## Step 8 # MUST      ## Step 8 # MUST
8075      my $i = 0;      my $i = 0;
8076      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8077      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8078      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8079        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8080        $input = $get_wrapper->($input);
8081        $p->{set_nc} = sub {
8082        my $self = shift;        my $self = shift;
8083    
8084        pop @{$self->{prev_char}};        my $char = '';
8085        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8086            $char = $self->{next_nc};
8087            delete $self->{next_nc};
8088            $self->{nc} = ord $char;
8089          } else {
8090            $self->{char_buffer} = '';
8091            $self->{char_buffer_pos} = 0;
8092            
8093            my $count = $input->manakai_read_until
8094                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8095                 $self->{char_buffer_pos});
8096            if ($count) {
8097              $self->{line_prev} = $self->{line};
8098              $self->{column_prev} = $self->{column};
8099              $self->{column}++;
8100              $self->{nc}
8101                  = ord substr ($self->{char_buffer},
8102                                $self->{char_buffer_pos}++, 1);
8103              return;
8104            }
8105            
8106            if ($input->read ($char, 1)) {
8107              $self->{nc} = ord $char;
8108            } else {
8109              $self->{nc} = -1;
8110              return;
8111            }
8112          }
8113    
8114          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8115          $p->{column}++;
8116    
8117        $self->{next_char} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
8118        $self->{next_char} = ord substr $$s, $i++, 1;          $p->{line}++;
8119        $column++;          $p->{column} = 0;
   
       if ($self->{next_char} == 0x000A) { # LF  
         $line++;  
         $column = 0;  
8120          !!!cp ('i1');          !!!cp ('i1');
8121        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8122          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8123          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8124          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8125          $column = 0;            $self->{next_nc} = $next;
8126            }
8127            $self->{nc} = 0x000A; # LF # MUST
8128            $p->{line}++;
8129            $p->{column} = 0;
8130          !!!cp ('i2');          !!!cp ('i2');
8131        } elsif ($self->{next_char} > 0x10FFFF) {        } elsif ($self->{nc} == 0x0000) { # NULL
         $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
         !!!cp ('i3');  
       } elsif ($self->{next_char} == 0x0000) { # NULL  
8132          !!!cp ('i4');          !!!cp ('i4');
8133          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8134          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8135        }        }
8136      };      };
8137      $p->{prev_char} = [-1, -1, -1];  
8138      $p->{next_char} = -1;      $p->{read_until} = sub {
8139              #my ($scalar, $specials_range, $offset) = @_;
8140          return 0 if defined $p->{next_nc};
8141    
8142          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8143          my $offset = $_[2] || 0;
8144          
8145          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8146            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8147            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8148              substr ($_[0], $offset)
8149                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8150              my $count = $+[0] - $-[0];
8151              if ($count) {
8152                $p->{column} += $count;
8153                $p->{char_buffer_pos} += $count;
8154                $p->{line_prev} = $p->{line};
8155                $p->{column_prev} = $p->{column} - 1;
8156                $p->{nc} = -1;
8157              }
8158              return $count;
8159            } else {
8160              return 0;
8161            }
8162          } else {
8163            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8164            if ($count) {
8165              $p->{column} += $count;
8166              $p->{column_prev} += $count;
8167              $p->{nc} = -1;
8168            }
8169            return $count;
8170          }
8171        }; # $p->{read_until}
8172    
8173      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8174        my (%opt) = @_;        my (%opt) = @_;
8175        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8176          my $column = $opt{column};
8177          if (defined $opt{token} and defined $opt{token}->{line}) {
8178            $line = $opt{token}->{line};
8179            $column = $opt{token}->{column};
8180          }
8181          warn "Parse error ($opt{type}) at line $line column $column\n";
8182      };      };
8183      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8184        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8185      };      };
8186            
8187        my $char_onerror = sub {
8188          my (undef, $type, %opt) = @_;
8189          $ponerror->(layer => 'encode',
8190                      line => $p->{line}, column => $p->{column} + 1,
8191                      %opt, type => $type);
8192        }; # $char_onerror
8193        $input->onerror ($char_onerror);
8194    
8195      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8196      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8197    
# Line 6546  sub set_inner_html ($$$) { Line 8213  sub set_inner_html ($$$) {
8213          unless defined $p->{content_model};          unless defined $p->{content_model};
8214          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8215    
8216      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8217          ## TODO: Foreign element OK?
8218    
8219      ## Step 3      ## Step 3
8220      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6556  sub set_inner_html ($$$) { Line 8224  sub set_inner_html ($$$) {
8224      $doc->append_child ($root);      $doc->append_child ($root);
8225    
8226      ## Step 5 # MUST      ## Step 5 # MUST
8227      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8228    
8229      undef $p->{head_element};      undef $p->{head_element};
8230        undef $p->{head_element_inserted};
8231    
8232      ## Step 6 # MUST      ## Step 6 # MUST
8233      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6602  sub set_inner_html ($$$) { Line 8271  sub set_inner_html ($$$) {
8271      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8272    
8273      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8274    
8275        delete $p->{parse_error}; # delete loop
8276    } else {    } else {
8277      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";
8278    }    }

Legend:
Removed from v.1.120  
changed lines
  Added in v.1.203

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24