/[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.200 by wakaba, Sat Oct 4 11:32:15 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      BODY_EL |
90      HTML_EL |
91      TABLE_CELL_EL |
92      TABLE_ROW_EL |
93      TABLE_ROW_GROUP_EL
94    }
95    
96    sub SCOPING_EL () {
97      BUTTON_EL |
98      CAPTION_EL |
99      HTML_EL |
100      TABLE_EL |
101      TABLE_CELL_EL |
102      MISC_SCOPING_EL
103    }
104    
105    sub TABLE_SCOPING_EL () {
106      HTML_EL |
107      TABLE_EL
108    }
109    
110    sub TABLE_ROWS_SCOPING_EL () {
111      HTML_EL |
112      TABLE_ROW_GROUP_EL
113    }
114    
115    sub TABLE_ROW_SCOPING_EL () {
116      HTML_EL |
117      TABLE_ROW_EL
118    }
119    
120    sub SPECIAL_EL () {
121      ADDRESS_EL |
122      BODY_EL |
123      DIV_EL |
124    
125      DD_EL |
126      DT_EL |
127      LI_EL |
128      P_EL |
129    
130      FORM_EL |
131      FRAMESET_EL |
132      HEADING_EL |
133      OPTION_EL |
134      OPTGROUP_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 $c1_entity_char = {  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 $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          my $count = $input->manakai_read_until
697             ($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      $self->{next_char} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
708      $self->{next_char} = ord substr $$s, $i++, 1;          $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);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998          !!!cp (221);          !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011          if ($self->{nc} == 0x005D) { # ]
3012            !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034    die "$0: _get_next_token: unexpected case";          ## Stay in the state.
3035  } # _get_next_token          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039  sub _tokenize_attempt_to_consume_an_entity ($$$) {        ## ISSUE: "text tokens" in spec.
3040    my ($self, $in_attr, $additional) = @_;      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041          if ($self->{nc} == 0x005D) { # ]
3042            !!!cp (221.5);
3043            $self->{state} = CDATA_SECTION_MSE2_STATE;
3044            !!!next-input-character;
3045            redo A;
3046          } else {
3047            !!!cp (221.6);
3048            $self->{ct}->{data} .= ']';
3049            $self->{state} = CDATA_SECTION_STATE;
3050            ## Reconsume.
3051            redo A;
3052          }
3053        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if ($self->{nc} == 0x003E) { # >
3055            $self->{state} = DATA_STATE;
3056            !!!next-input-character;
3057            if (length $self->{ct}->{data}) { # character
3058              !!!cp (221.7);
3059              !!!emit ($self->{ct}); # character
3060            } else {
3061              !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063            }
3064            redo A;
3065          } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!cp (221.9); # character
3067            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068            ## Stay in the state.
3069            !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112          }
3113    
3114    my ($l, $c) = ($self->{line_prev}, $self->{column_prev});        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156    if ({          ## NOTE: According to the spec algorithm, nothing is returned,
3157         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,          ## and then "&#" is appended to the parent element or the attribute
3158         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR          ## value in the later processing.
3159         $additional => 1,  
3160        }->{$self->{next_char}}) {          if ($self->{prev_state} == DATA_STATE) {
3161      !!!cp (1001);            !!!cp (1019);
3162      ## Don't consume            $self->{state} = $self->{prev_state};
3163      ## No error            ## Reconsume.
3164      return undef;            !!!emit ({type => CHARACTER_TOKEN,
3165    } elsif ($self->{next_char} == 0x0023) { # #                      data => '&#',
3166      !!!next-input-character;                      line => $self->{line_prev},
3167      if ($self->{next_char} == 0x0078 or # x                      column => $self->{column_prev} - 1,
3168          $self->{next_char} == 0x0058) { # X                     });
3169        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;  
3170          } else {          } else {
3171            !!!cp (1007);            !!!cp (993);
3172            !!!parse-error (type => 'no refc', line => $l, column => $c);            $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176          }          }
3177          }
3178          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {      } elsif ($self->{state} == NCR_NUM_STATE) {
3179            !!!cp (1008);        if (0x0030 <= $self->{nc} and
3180            !!!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  
3181          !!!cp (1012);          !!!cp (1012);
3182          $code *= 10;          $self->{s_kwd} *= 10;
3183          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3184                    
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187        }          redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3189          !!!cp (1013);          !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193          !!!cp (1014);          !!!cp (1014);
3194          !!!parse-error (type => 'no refc', line => $l, column => $c);          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203          !!!cp (1015);          !!!cp (1015);
3204          !!!parse-error (type => (sprintf 'invalid character reference:U+%04X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3205          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3209          !!!cp (1016);          !!!cp (1016);
3210          !!!parse-error (type => (sprintf 'invalid character reference:U-%08X', $code), line => $l, column => $c);          !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $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};  
3214        }        }
3215          
3216        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,        if ($self->{prev_state} == DATA_STATE) {
3217                line => $l, column => $c,          !!!cp (992);
3218               };          $self->{state} = $self->{prev_state};
3219      } else {          ## Reconsume.
3220        !!!cp (1019);          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221        !!!parse-error (type => 'bare nero', line => $l, column => $c);                    line => $l, column => $c,
3222        !!!back-next-input-character ($self->{next_char});                   });
3223        $self->{next_char} = 0x0023; # #          redo A;
3224        return undef;        } else {
3225      }          !!!cp (991);
3226    } elsif ((0x0041 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3227              $self->{next_char} <= 0x005A) or          $self->{ca}->{has_reference} = 1;
3228             (0x0061 <= $self->{next_char} and          $self->{state} = $self->{prev_state};
3229              $self->{next_char} <= 0x007A)) {          ## Reconsume.
3230      my $entity_name = chr $self->{next_char};          redo A;
3231      !!!next-input-character;        }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233      my $value = $entity_name;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234      my $match = 0;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235      require Whatpm::_NamedEntityList;            (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236      our $EntityChar;          # 0..9, A..F, a..f
3237            !!!cp (990);
3238      while (length $entity_name < 10 and          $self->{state} = HEXREF_HEX_STATE;
3239             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} = 0;
3240             ((0x0041 <= $self->{next_char} and # a          ## Reconsume.
3241               $self->{next_char} <= 0x005A) or # x          redo A;
3242              (0x0061 <= $self->{next_char} and # a        } else {
3243               $self->{next_char} <= 0x007A) or # z          !!!parse-error (type => 'bare hcro',
3244              (0x0030 <= $self->{next_char} and # 0                          line => $self->{line_prev},
3245               $self->{next_char} <= 0x0039) or # 9                          column => $self->{column_prev} - 2);
3246              $self->{next_char} == 0x003B)) { # ;  
3247        $entity_name .= chr $self->{next_char};          ## NOTE: According to the spec algorithm, nothing is returned,
3248        if (defined $EntityChar->{$entity_name}) {          ## and then "&#" followed by "X" or "x" is appended to the parent
3249          if ($self->{next_char} == 0x003B) { # ;          ## element or the attribute value in the later processing.
3250            !!!cp (1020);  
3251            $value = $EntityChar->{$entity_name};          if ($self->{prev_state} == DATA_STATE) {
3252            $match = 1;            !!!cp (1005);
3253            !!!next-input-character;            $self->{state} = $self->{prev_state};
3254            last;            ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261          } else {          } else {
3262            !!!cp (1021);            !!!cp (989);
3263            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264            $match = -1;            $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322          }
3323    
3324          if ($self->{prev_state} == DATA_STATE) {
3325            !!!cp (988);
3326            $self->{state} = $self->{prev_state};
3327            ## Reconsume.
3328            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329                      line => $l, column => $c,
3330                     });
3331            redo A;
3332          } else {
3333            !!!cp (987);
3334            $self->{ca}->{value} .= chr $code;
3335            $self->{ca}->{has_reference} = 1;
3336            $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338            redo A;
3339          }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341          if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343              ((0x0041 <= $self->{nc} and # a
3344                $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346                $self->{nc} <= 0x007A) or # z
3347               (0x0030 <= $self->{nc} and # 0
3348                $self->{nc} <= 0x0039) or # 9
3349               $self->{nc} == 0x003B)) { # ;
3350            our $EntityChar;
3351            $self->{s_kwd} .= chr $self->{nc};
3352            if (defined $EntityChar->{$self->{s_kwd}}) {
3353              if ($self->{nc} == 0x003B) { # ;
3354                !!!cp (1020);
3355                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356                $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374            }
3375          }
3376    
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396          }          }
3397        } else {        } else {
3398          !!!cp (1022);          !!!cp (1026);
3399          $value .= chr $self->{next_char};          !!!parse-error (type => 'bare ero',
3400          $match *= 2;                          line => $self->{line_prev},
3401          !!!next-input-character;                          column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404        }        }
3405      }    
3406              ## NOTE: In these cases, when a character reference is found,
3407      if ($match > 0) {        ## it is consumed and a character token is returned, or, otherwise,
3408        !!!cp (1023);        ## nothing is consumed and returned, according to the spec algorithm.
3409        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,        ## In this implementation, anything that has been examined by the
3410                line => $l, column => $c,        ## tokenizer is appended to the parent element or the attribute value
3411               };        ## as string, either literal string when no character reference or
3412      } elsif ($match < 0) {        ## entity-replaced string otherwise, in this stage, since any characters
3413        !!!parse-error (type => 'no refc', line => $l, column => $c);        ## that would not be consumed are appended in the data state or in an
3414        if ($in_attr and $match < -1) {        ## appropriate attribute value state anyway.
3415          !!!cp (1024);  
3416          return {type => CHARACTER_TOKEN, data => '&'.$entity_name,        if ($self->{prev_state} == DATA_STATE) {
3417                  line => $l, column => $c,          !!!cp (986);
3418                 };          $self->{state} = $self->{prev_state};
3419        } else {          ## Reconsume.
3420          !!!cp (1025);          !!!emit ({type => CHARACTER_TOKEN,
3421          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,                    data => $data,
3422                  line => $l, column => $c,                    line => $self->{line_prev},
3423                 };                    column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!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,  
              };  
3436      }      }
3437    } else {    } # A  
3438      !!!cp (1027);  
3439      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3440      !!!parse-error (type => 'bare ero', line => $l, column => $c);  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 2462  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 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 3090  sub _tree_construction_main ($) { Line 4096  sub _tree_construction_main ($) {
4096        my $formatting_element;        my $formatting_element;
4097        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4098        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4099          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4100              !!!cp ('t52');
4101              last AFE;
4102            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4103                         eq $tag_name) {
4104            !!!cp ('t51');            !!!cp ('t51');
4105            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4106            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4107            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4108          }          }
4109        } # AFE        } # AFE
4110        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4111          !!!cp ('t53');          !!!cp ('t53');
4112          !!!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);
4113          ## Ignore the token          ## Ignore the token
4114          !!!next-token;          !!!next-token;
4115          return;          return;
# Line 3119  sub _tree_construction_main ($) { Line 4126  sub _tree_construction_main ($) {
4126              last INSCOPE;              last INSCOPE;
4127            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4128              !!!cp ('t55');              !!!cp ('t55');
4129              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},              !!!parse-error (type => 'unmatched end tag',
4130                                text => $token->{tag_name},
4131                              token => $end_tag_token);                              token => $end_tag_token);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
4134              return;              return;
4135            }            }
4136          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4137            !!!cp ('t56');            !!!cp ('t56');
4138            $in_scope = 0;            $in_scope = 0;
4139          }          }
4140        } # INSCOPE        } # INSCOPE
4141        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4142          !!!cp ('t57');          !!!cp ('t57');
4143          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name},          !!!parse-error (type => 'unmatched end tag',
4144                            text => $token->{tag_name},
4145                          token => $end_tag_token);                          token => $end_tag_token);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
# Line 3143  sub _tree_construction_main ($) { Line 4149  sub _tree_construction_main ($) {
4149        }        }
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!cp ('t58');          !!!cp ('t58');
4152          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1],          !!!parse-error (type => 'not closed',
4153                            text => $self->{open_elements}->[-1]->[0]
4154                                ->manakai_local_name,
4155                          token => $end_tag_token);                          token => $end_tag_token);
4156        }        }
4157                
# Line 3152  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4161        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4162          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4163          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4164              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4165              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4166               $scoping_category->{$node->[1]})) { ## Scoping is redundant, maybe               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4167            !!!cp ('t59');            !!!cp ('t59');
4168            $furthest_block = $node;            $furthest_block = $node;
4169            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3241  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } # S7          } # S7  
4250                
4251        ## Step 8        ## Step 8
4252        if ({        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
            table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
           }->{$common_ancestor_node->[1]}) {  
4253          my $foster_parent_element;          my $foster_parent_element;
4254          my $next_sibling;          my $next_sibling;
4255                           OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
4256                             if ($self->{open_elements}->[$_]->[1] eq 'table') {            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4257                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4258                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4259                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 3320  sub _tree_construction_main ($) { Line 4326  sub _tree_construction_main ($) {
4326    
4327    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4328      my $child = shift;      my $child = shift;
4329      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
         }->{$self->{open_elements}->[-1]->[1]}) {  
4330        # MUST        # MUST
4331        my $foster_parent_element;        my $foster_parent_element;
4332        my $next_sibling;        my $next_sibling;
4333                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4334                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4335                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4336                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4337                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3352  sub _tree_construction_main ($) { Line 4356  sub _tree_construction_main ($) {
4356      }      }
4357    }; # $insert_to_foster    }; # $insert_to_foster
4358    
4359    B: {    B: while (1) {
4360      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4361        !!!cp ('t73');        !!!cp ('t73');
4362        !!!parse-error (type => 'DOCTYPE in the middle', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363        ## Ignore the token        ## Ignore the token
4364        ## Stay in the phase        ## Stay in the phase
4365        !!!next-token;        !!!next-token;
4366        redo B;        next B;
4367      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4368               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4369        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370          !!!cp ('t79');          !!!cp ('t79');
4371          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4373        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          !!!cp ('t80');          !!!cp ('t80');
4375          !!!parse-error (type => 'after html:html', token => $token);          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377        } else {        } else {
4378          !!!cp ('t81');          !!!cp ('t81');
# Line 3385  sub _tree_construction_main ($) { Line 4389  sub _tree_construction_main ($) {
4389               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4390          }          }
4391        }        }
4392          !!!nack ('t84.1');
4393        !!!next-token;        !!!next-token;
4394        redo B;        next B;
4395      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4396        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4397        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3400  sub _tree_construction_main ($) { Line 4405  sub _tree_construction_main ($) {
4405          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4406        }        }
4407        !!!next-token;        !!!next-token;
4408        redo B;        next B;
4409      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4410        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4411          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4412            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4413            !!!next-token;
4414            next B;
4415          } elsif ($token->{type} == START_TAG_TOKEN) {
4416            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4417                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4418                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4419                ($token->{tag_name} eq 'svg' and
4420                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4421              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4422              !!!cp ('t87.2');
4423              #
4424            } elsif ({
4425                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4426                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4427                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4428                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4429                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4430                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4431                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4432                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4433                     }->{$token->{tag_name}}) {
4434              !!!cp ('t87.2');
4435              !!!parse-error (type => 'not closed',
4436                              text => $self->{open_elements}->[-1]->[0]
4437                                  ->manakai_local_name,
4438                              token => $token);
4439    
4440              pop @{$self->{open_elements}}
4441                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4442    
4443              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4444              ## Reprocess.
4445              next B;
4446            } else {
4447              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4448              my $tag_name = $token->{tag_name};
4449              if ($nsuri eq $SVG_NS) {
4450                $tag_name = {
4451                   altglyph => 'altGlyph',
4452                   altglyphdef => 'altGlyphDef',
4453                   altglyphitem => 'altGlyphItem',
4454                   animatecolor => 'animateColor',
4455                   animatemotion => 'animateMotion',
4456                   animatetransform => 'animateTransform',
4457                   clippath => 'clipPath',
4458                   feblend => 'feBlend',
4459                   fecolormatrix => 'feColorMatrix',
4460                   fecomponenttransfer => 'feComponentTransfer',
4461                   fecomposite => 'feComposite',
4462                   feconvolvematrix => 'feConvolveMatrix',
4463                   fediffuselighting => 'feDiffuseLighting',
4464                   fedisplacementmap => 'feDisplacementMap',
4465                   fedistantlight => 'feDistantLight',
4466                   feflood => 'feFlood',
4467                   fefunca => 'feFuncA',
4468                   fefuncb => 'feFuncB',
4469                   fefuncg => 'feFuncG',
4470                   fefuncr => 'feFuncR',
4471                   fegaussianblur => 'feGaussianBlur',
4472                   feimage => 'feImage',
4473                   femerge => 'feMerge',
4474                   femergenode => 'feMergeNode',
4475                   femorphology => 'feMorphology',
4476                   feoffset => 'feOffset',
4477                   fepointlight => 'fePointLight',
4478                   fespecularlighting => 'feSpecularLighting',
4479                   fespotlight => 'feSpotLight',
4480                   fetile => 'feTile',
4481                   feturbulence => 'feTurbulence',
4482                   foreignobject => 'foreignObject',
4483                   glyphref => 'glyphRef',
4484                   lineargradient => 'linearGradient',
4485                   radialgradient => 'radialGradient',
4486                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4487                   textpath => 'textPath',  
4488                }->{$tag_name} || $tag_name;
4489              }
4490    
4491              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4492    
4493              ## "adjust foreign attributes" - done in insert-element-f
4494    
4495              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4496    
4497              if ($self->{self_closing}) {
4498                pop @{$self->{open_elements}};
4499                !!!ack ('t87.3');
4500              } else {
4501                !!!cp ('t87.4');
4502              }
4503    
4504              !!!next-token;
4505              next B;
4506            }
4507          } elsif ($token->{type} == END_TAG_TOKEN) {
4508            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4509            !!!cp ('t87.5');
4510            #
4511          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4512            !!!cp ('t87.6');
4513            !!!parse-error (type => 'not closed',
4514                            text => $self->{open_elements}->[-1]->[0]
4515                                ->manakai_local_name,
4516                            token => $token);
4517    
4518            pop @{$self->{open_elements}}
4519                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4520    
4521            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4522    
4523            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524            ## Reprocess.
4525            next B;
4526          } else {
4527            die "$0: $token->{type}: Unknown token type";        
4528          }
4529        }
4530    
4531        if ($self->{insertion_mode} & HEAD_IMS) {
4532          if ($token->{type} == CHARACTER_TOKEN) {
4533            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535              !!!cp ('t88.2');              !!!cp ('t88.2');
4536              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537                #
4538            } else {            } else {
4539              !!!cp ('t88.1');              !!!cp ('t88.1');
4540              ## Ignore the token.              ## Ignore the token.
4541              !!!next-token;              #
             redo B;  
4542            }            }
4543            unless (length $token->{data}) {            unless (length $token->{data}) {
4544              !!!cp ('t88');              !!!cp ('t88');
4545              !!!next-token;              !!!next-token;
4546              redo B;              next B;
4547            }            }
4548    ## TODO: set $token->{column} appropriately
4549          }          }
4550    
4551          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552            !!!cp ('t89');            !!!cp ('t89');
4553            ## As if <head>            ## As if <head>
4554            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4555            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4556            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4557                  [$self->{head_element}, $el_category->{head}];
4558    
4559            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4560            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3435  sub _tree_construction_main ($) { Line 4564  sub _tree_construction_main ($) {
4564            !!!cp ('t90');            !!!cp ('t90');
4565            ## As if </noscript>            ## As if </noscript>
4566            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4567            !!!parse-error (type => 'in noscript:#character', token => $token);            !!!parse-error (type => 'in noscript:#text', token => $token);
4568                        
4569            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4570            ## As if </head>            ## As if </head>
# Line 3451  sub _tree_construction_main ($) { Line 4580  sub _tree_construction_main ($) {
4580            !!!cp ('t92');            !!!cp ('t92');
4581          }          }
4582    
4583              ## "after head" insertion mode          ## "after head" insertion mode
4584              ## As if <body>          ## As if <body>
4585              !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
4586              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4587              ## reprocess          ## reprocess
4588              redo B;          next B;
4589            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4590              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4591                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4592                  !!!cp ('t93');              !!!cp ('t93');
4593                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes}, $token);              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4594                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4595                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4596                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4597                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4598                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4599                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4600                  !!!cp ('t94');              !!!next-token;
4601                  #              next B;
4602                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603                  !!!cp ('t95');              !!!cp ('t93.2');
4604                  !!!parse-error (type => 'in head:head', token => $token); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4605                  ## Ignore the token                              token => $token);
4606                  !!!next-token;              ## Ignore the token
4607                  redo B;              !!!nack ('t93.3');
4608                }              !!!next-token;
4609              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4610                !!!cp ('t96');            } else {
4611                ## As if <head>              !!!cp ('t95');
4612                !!!create-element ($self->{head_element}, 'head',, $token);              !!!parse-error (type => 'in head:head',
4613                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4614                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4615                !!!nack ('t95.1');
4616                !!!next-token;
4617                next B;
4618              }
4619            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620              !!!cp ('t96');
4621              ## As if <head>
4622              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4623              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4624              push @{$self->{open_elements}},
4625                  [$self->{head_element}, $el_category->{head}];
4626    
4627                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4628                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4629              } else {          } else {
4630                !!!cp ('t97');            !!!cp ('t97');
4631              }          }
4632    
4633              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
4634                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4635                  !!!cp ('t98');                  !!!cp ('t98');
4636                  ## As if </noscript>                  ## As if </noscript>
4637                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4638                  !!!parse-error (type => 'in noscript:base', token => $token);                  !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                                
4641                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4642                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3506  sub _tree_construction_main ($) { Line 4647  sub _tree_construction_main ($) {
4647                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4648                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649                  !!!cp ('t100');                  !!!cp ('t100');
4650                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4651                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4652                    push @{$self->{open_elements}},
4653                        [$self->{head_element}, $el_category->{head}];
4654                } else {                } else {
4655                  !!!cp ('t101');                  !!!cp ('t101');
4656                }                }
4657                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4658                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}};
               pop @{$self->{open_elements}} # <head>  
                   if $self->{insertion_mode} == AFTER_HEAD_IM;  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'link') {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} == AFTER_HEAD_IM) {  
                 !!!cp ('t102');  
                 !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               } else {  
                 !!!cp ('t103');  
               }  
               !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
4659                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4660                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661                  !!!nack ('t101.1');
4662                !!!next-token;                !!!next-token;
4663                redo B;                next B;
4664            } elsif ($token->{tag_name} eq 'link') {
4665              ## NOTE: There is a "as if in head" code clone.
4666              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                !!!cp ('t102');
4668                !!!parse-error (type => 'after head',
4669                                text => $token->{tag_name}, token => $token);
4670                push @{$self->{open_elements}},
4671                    [$self->{head_element}, $el_category->{head}];
4672              } else {
4673                !!!cp ('t103');
4674              }
4675              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4676              pop @{$self->{open_elements}};
4677              pop @{$self->{open_elements}} # <head>
4678                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4679              !!!ack ('t103.1');
4680              !!!next-token;
4681              next B;
4682            } elsif ($token->{tag_name} eq 'command' or
4683                     $token->{tag_name} eq 'eventsource') {
4684              if ($self->{insertion_mode} == IN_HEAD_IM) {
4685                ## NOTE: If the insertion mode at the time of the emission
4686                ## of the token was "before head", $self->{insertion_mode}
4687                ## is already changed to |IN_HEAD_IM|.
4688    
4689                ## NOTE: There is a "as if in head" code clone.
4690                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4691                pop @{$self->{open_elements}};
4692                pop @{$self->{open_elements}} # <head>
4693                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4694                !!!ack ('t103.2');
4695                !!!next-token;
4696                next B;
4697              } else {
4698                ## NOTE: "in head noscript" or "after head" insertion mode
4699                ## - in these cases, these tags are treated as same as
4700                ## normal in-body tags.
4701                !!!cp ('t103.3');
4702                #
4703              }
4704              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4705                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4706                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4707                  !!!cp ('t104');                  !!!cp ('t104');
4708                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4709                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4710                    push @{$self->{open_elements}},
4711                        [$self->{head_element}, $el_category->{head}];
4712                } else {                } else {
4713                  !!!cp ('t105');                  !!!cp ('t105');
4714                }                }
4715                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4716                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}};
4717    
4718                unless ($self->{confident}) {                unless ($self->{confident}) {
4719                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4720                    !!!cp ('t106');                    !!!cp ('t106');
4721                      ## NOTE: Whether the encoding is supported or not is handled
4722                      ## in the {change_encoding} callback.
4723                    $self->{change_encoding}                    $self->{change_encoding}
4724                        ->($self, $token->{attributes}->{charset}->{value},                        ->($self, $token->{attributes}->{charset}->{value},
4725                           $token);                           $token);
# Line 3556  sub _tree_construction_main ($) { Line 4729  sub _tree_construction_main ($) {
4729                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4730                                                 ->{has_reference});                                                 ->{has_reference});
4731                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4732                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4733                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4734                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4735                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4736                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4737                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4738                      !!!cp ('t107');                      !!!cp ('t107');
4739                        ## NOTE: Whether the encoding is supported or not is handled
4740                        ## in the {change_encoding} callback.
4741                      $self->{change_encoding}                      $self->{change_encoding}
4742                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4743                             $token);                             $token);
# Line 3593  sub _tree_construction_main ($) { Line 4768  sub _tree_construction_main ($) {
4768    
4769                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4770                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4771                  !!!ack ('t110.1');
4772                !!!next-token;                !!!next-token;
4773                redo B;                next B;
4774              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
4775                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4776                  !!!cp ('t111');                  !!!cp ('t111');
4777                  ## As if </noscript>                  ## As if </noscript>
4778                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4779                  !!!parse-error (type => 'in noscript:title', token => $token);                  !!!parse-error (type => 'in noscript', text => 'title',
4780                                    token => $token);
4781                                
4782                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4783                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4784                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                  !!!cp ('t112');                  !!!cp ('t112');
4786                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4787                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4788                    push @{$self->{open_elements}},
4789                        [$self->{head_element}, $el_category->{head}];
4790                } else {                } else {
4791                  !!!cp ('t113');                  !!!cp ('t113');
4792                }                }
# Line 3618  sub _tree_construction_main ($) { Line 4797  sub _tree_construction_main ($) {
4797                $parse_rcdata->(RCDATA_CONTENT_MODEL);                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4798                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4799                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4800                redo B;                next B;
4801              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4802                         $token->{tag_name} eq 'noframes') {
4803                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4804                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4805                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4806                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4807                  !!!cp ('t114');                  !!!cp ('t114');
4808                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4809                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4810                    push @{$self->{open_elements}},
4811                        [$self->{head_element}, $el_category->{head}];
4812                } else {                } else {
4813                  !!!cp ('t115');                  !!!cp ('t115');
4814                }                }
4815                $parse_rcdata->(CDATA_CONTENT_MODEL);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4816                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4817                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4818                redo B;                next B;
4819              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4820                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4821                  !!!cp ('t116');                  !!!cp ('t116');
4822                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4823                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4824                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4825                    !!!nack ('t116.1');
4826                  !!!next-token;                  !!!next-token;
4827                  redo B;                  next B;
4828                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4829                  !!!cp ('t117');                  !!!cp ('t117');
4830                  !!!parse-error (type => 'in noscript:noscript', token => $token);                  !!!parse-error (type => 'in noscript', text => 'noscript',
4831                                    token => $token);
4832                  ## Ignore the token                  ## Ignore the token
4833                    !!!nack ('t117.1');
4834                  !!!next-token;                  !!!next-token;
4835                  redo B;                  next B;
4836                } else {                } else {
4837                  !!!cp ('t118');                  !!!cp ('t118');
4838                  #                  #
# Line 3657  sub _tree_construction_main ($) { Line 4842  sub _tree_construction_main ($) {
4842                  !!!cp ('t119');                  !!!cp ('t119');
4843                  ## As if </noscript>                  ## As if </noscript>
4844                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4845                  !!!parse-error (type => 'in noscript:script', token => $token);                  !!!parse-error (type => 'in noscript', text => 'script',
4846                                    token => $token);
4847                                
4848                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4849                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4850                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4851                  !!!cp ('t120');                  !!!cp ('t120');
4852                  !!!parse-error (type => 'after head:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'after head',
4853                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4854                    push @{$self->{open_elements}},
4855                        [$self->{head_element}, $el_category->{head}];
4856                } else {                } else {
4857                  !!!cp ('t121');                  !!!cp ('t121');
4858                }                }
# Line 3673  sub _tree_construction_main ($) { Line 4861  sub _tree_construction_main ($) {
4861                $script_start_tag->();                $script_start_tag->();
4862                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4863                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4864                redo B;                next B;
4865              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4866                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4867                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4868                  !!!cp ('t122');                  !!!cp ('t122');
4869                  ## As if </noscript>                  ## As if </noscript>
4870                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4871                  !!!parse-error (type => 'in noscript:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in noscript',
4872                                    text => $token->{tag_name}, token => $token);
4873                                    
4874                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4875                  ## As if </head>                  ## As if </head>
# Line 3707  sub _tree_construction_main ($) { Line 4896  sub _tree_construction_main ($) {
4896                } else {                } else {
4897                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4898                }                }
4899                  !!!nack ('t127.1');
4900                !!!next-token;                !!!next-token;
4901                redo B;                next B;
4902              } else {              } else {
4903                !!!cp ('t128');                !!!cp ('t128');
4904                #                #
# Line 3718  sub _tree_construction_main ($) { Line 4908  sub _tree_construction_main ($) {
4908                !!!cp ('t129');                !!!cp ('t129');
4909                ## As if </noscript>                ## As if </noscript>
4910                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4911                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
4912                                  text => $token->{tag_name}, token => $token);
4913                                
4914                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4915                ## As if </head>                ## As if </head>
# Line 3740  sub _tree_construction_main ($) { Line 4931  sub _tree_construction_main ($) {
4931              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
4932              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4933              ## reprocess              ## reprocess
4934              redo B;              !!!ack-later;
4935                next B;
4936            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4937              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4938                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4939                  !!!cp ('t132');                  !!!cp ('t132');
4940                  ## As if <head>                  ## As if <head>
4941                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4942                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4943                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4944                        [$self->{head_element}, $el_category->{head}];
4945    
4946                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4947                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4948                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4949                  !!!next-token;                  !!!next-token;
4950                  redo B;                  next B;
4951                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4952                  !!!cp ('t133');                  !!!cp ('t133');
4953                  ## As if </noscript>                  ## As if </noscript>
4954                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4955                  !!!parse-error (type => 'in noscript:/head', token => $token);                  !!!parse-error (type => 'in noscript:/',
4956                                    text => 'head', token => $token);
4957                                    
4958                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4959                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4960                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4961                  !!!next-token;                  !!!next-token;
4962                  redo B;                  next B;
4963                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4964                  !!!cp ('t134');                  !!!cp ('t134');
4965                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4966                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4967                  !!!next-token;                  !!!next-token;
4968                  redo B;                  next B;
4969                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4970                    !!!cp ('t134.1');
4971                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4972                                    token => $token);
4973                    ## Ignore the token
4974                    !!!next-token;
4975                    next B;
4976                } else {                } else {
4977                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
4978                }                }
4979              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4980                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3782  sub _tree_construction_main ($) { Line 4982  sub _tree_construction_main ($) {
4982                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4983                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4984                  !!!next-token;                  !!!next-token;
4985                  redo B;                  next B;
4986                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4987                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4988                  !!!cp ('t137');                  !!!cp ('t137');
4989                  !!!parse-error (type => 'unmatched end tag:noscript', token => $token);                  !!!parse-error (type => 'unmatched end tag',
4990                                    text => 'noscript', token => $token);
4991                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4992                  !!!next-token;                  !!!next-token;
4993                  redo B;                  next B;
4994                } else {                } else {
4995                  !!!cp ('t138');                  !!!cp ('t138');
4996                  #                  #
# Line 3796  sub _tree_construction_main ($) { Line 4998  sub _tree_construction_main ($) {
4998              } elsif ({              } elsif ({
4999                        body => 1, html => 1,                        body => 1, html => 1,
5000                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5001                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5002                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
5003                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!create-element ($self->{head_element}, 'head',, $token);  
                 $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) {  
5004                  !!!cp ('t140');                  !!!cp ('t140');
5005                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5006                                    text => $token->{tag_name}, token => $token);
5007                  ## Ignore the token                  ## Ignore the token
5008                  !!!next-token;                  !!!next-token;
5009                  redo B;                  next B;
5010                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5011                    !!!cp ('t140.1');
5012                    !!!parse-error (type => 'unmatched end tag',
5013                                    text => $token->{tag_name}, token => $token);
5014                    ## Ignore the token
5015                    !!!next-token;
5016                    next B;
5017                } else {                } else {
5018                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5019                }                }
5020                              } elsif ($token->{tag_name} eq 'p') {
5021                #                !!!cp ('t142');
5022              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5023                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5024                       }->{$token->{tag_name}}) {                ## Ignore the token
5025                  !!!next-token;
5026                  next B;
5027                } elsif ($token->{tag_name} eq 'br') {
5028                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5029                  !!!cp ('t142');                  !!!cp ('t142.2');
5030                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5031                  !!!create-element ($self->{head_element}, 'head',, $token);                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5032                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5033                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5034      
5035                    ## Reprocess in the "after head" insertion mode...
5036                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5037                    !!!cp ('t143.2');
5038                    ## As if </head>
5039                    pop @{$self->{open_elements}};
5040                    $self->{insertion_mode} = AFTER_HEAD_IM;
5041      
5042                    ## Reprocess in the "after head" insertion mode...
5043                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5044                    !!!cp ('t143.3');
5045                    ## ISSUE: Two parse errors for <head><noscript></br>
5046                    !!!parse-error (type => 'unmatched end tag',
5047                                    text => 'br', token => $token);
5048                    ## As if </noscript>
5049                    pop @{$self->{open_elements}};
5050                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5051    
5052                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5053                } else {                  ## As if </head>
5054                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5055                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5056    
5057                #                  ## Reprocess in the "after head" insertion mode...
5058              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5059                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5060                  #                  #
5061                } else {                } else {
5062                  !!!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;  
5063                }                }
5064    
5065                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5066                  !!!parse-error (type => 'unmatched end tag',
5067                                  text => 'br', token => $token);
5068                  ## Ignore the token
5069                  !!!next-token;
5070                  next B;
5071                } else {
5072                  !!!cp ('t145');
5073                  !!!parse-error (type => 'unmatched end tag',
5074                                  text => $token->{tag_name}, token => $token);
5075                  ## Ignore the token
5076                  !!!next-token;
5077                  next B;
5078              }              }
5079    
5080              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5081                !!!cp ('t146');                !!!cp ('t146');
5082                ## As if </noscript>                ## As if </noscript>
5083                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5084                !!!parse-error (type => 'in noscript:/'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'in noscript:/',
5085                                  text => $token->{tag_name}, token => $token);
5086                                
5087                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5088                ## As if </head>                ## As if </head>
# Line 3866  sub _tree_construction_main ($) { Line 5098  sub _tree_construction_main ($) {
5098              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5099  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5100                !!!cp ('t148');                !!!cp ('t148');
5101                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5102                                  text => $token->{tag_name}, token => $token);
5103                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5104                !!!next-token;                !!!next-token;
5105                redo B;                next B;
5106              } else {              } else {
5107                !!!cp ('t149');                !!!cp ('t149');
5108              }              }
# Line 3879  sub _tree_construction_main ($) { Line 5112  sub _tree_construction_main ($) {
5112              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
5113              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5114              ## reprocess              ## reprocess
5115              redo B;              next B;
5116        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5117          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5118            !!!cp ('t149.1');            !!!cp ('t149.1');
5119    
5120            ## NOTE: As if <head>            ## NOTE: As if <head>
5121            !!!create-element ($self->{head_element}, 'head',, $token);            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5122            $self->{open_elements}->[-1]->[0]->append_child            $self->{open_elements}->[-1]->[0]->append_child
5123                ($self->{head_element});                ($self->{head_element});
5124            #push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            #push @{$self->{open_elements}},
5125              #    [$self->{head_element}, $el_category->{head}];
5126            #$self->{insertion_mode} = IN_HEAD_IM;            #$self->{insertion_mode} = IN_HEAD_IM;
5127            ## NOTE: Reprocess.            ## NOTE: Reprocess.
5128    
# Line 3932  sub _tree_construction_main ($) { Line 5166  sub _tree_construction_main ($) {
5166          !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
5167          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
5168          ## NOTE: Reprocess.          ## NOTE: Reprocess.
5169          redo B;          next B;
5170        } else {        } else {
5171          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5172        }        }
   
           ## ISSUE: An issue in the spec.  
5173      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5174            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5175              !!!cp ('t150');              !!!cp ('t150');
# Line 3947  sub _tree_construction_main ($) { Line 5179  sub _tree_construction_main ($) {
5179              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5180    
5181              !!!next-token;              !!!next-token;
5182              redo B;              next B;
5183            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5184              if ({              if ({
5185                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3957  sub _tree_construction_main ($) { Line 5189  sub _tree_construction_main ($) {
5189                  ## have an element in table scope                  ## have an element in table scope
5190                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5191                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5192                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5193                      !!!cp ('t151');                      !!!cp ('t151');
5194    
5195                      ## Close the cell                      ## Close the cell
5196                      !!!back-token; # <?>                      !!!back-token; # <x>
5197                      $token = {type => END_TAG_TOKEN, tag_name => $node->[1],                      $token = {type => END_TAG_TOKEN,
5198                                  tag_name => $node->[0]->manakai_local_name,
5199                                line => $token->{line},                                line => $token->{line},
5200                                column => $token->{column}};                                column => $token->{column}};
5201                      redo B;                      next B;
5202                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5203                      !!!cp ('t152');                      !!!cp ('t152');
5204                      ## ISSUE: This case can never be reached, maybe.                      ## ISSUE: This case can never be reached, maybe.
5205                      last;                      last;
# Line 3977  sub _tree_construction_main ($) { Line 5208  sub _tree_construction_main ($) {
5208    
5209                  !!!cp ('t153');                  !!!cp ('t153');
5210                  !!!parse-error (type => 'start tag not allowed',                  !!!parse-error (type => 'start tag not allowed',
5211                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5212                  ## Ignore the token                  ## Ignore the token
5213                    !!!nack ('t153.1');
5214                  !!!next-token;                  !!!next-token;
5215                  redo B;                  next B;
5216                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5217                  !!!parse-error (type => 'not closed:caption', token => $token);                  !!!parse-error (type => 'not closed', text => 'caption',
5218                                    token => $token);
5219                                    
5220                  ## NOTE: As if </caption>.                  ## NOTE: As if </caption>.
5221                  ## have a table element in table scope                  ## have a table element in table scope
# Line 3990  sub _tree_construction_main ($) { Line 5223  sub _tree_construction_main ($) {
5223                  INSCOPE: {                  INSCOPE: {
5224                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5225                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5226                      if ($node->[1] eq 'caption') {                      if ($node->[1] & CAPTION_EL) {
5227                        !!!cp ('t155');                        !!!cp ('t155');
5228                        $i = $_;                        $i = $_;
5229                        last INSCOPE;                        last INSCOPE;
5230                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5231                        !!!cp ('t156');                        !!!cp ('t156');
5232                        last;                        last;
5233                      }                      }
# Line 4004  sub _tree_construction_main ($) { Line 5235  sub _tree_construction_main ($) {
5235    
5236                    !!!cp ('t157');                    !!!cp ('t157');
5237                    !!!parse-error (type => 'start tag not allowed',                    !!!parse-error (type => 'start tag not allowed',
5238                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5239                    ## Ignore the token                    ## Ignore the token
5240                      !!!nack ('t157.1');
5241                    !!!next-token;                    !!!next-token;
5242                    redo B;                    next B;
5243                  } # INSCOPE                  } # INSCOPE
5244                                    
5245                  ## generate implied end tags                  ## generate implied end tags
5246                  while ({                  while ($self->{open_elements}->[-1]->[1]
5247                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5248                    !!!cp ('t158');                    !!!cp ('t158');
5249                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5250                  }                  }
5251    
5252                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5253                    !!!cp ('t159');                    !!!cp ('t159');
5254                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5255                                      text => $self->{open_elements}->[-1]->[0]
5256                                          ->manakai_local_name,
5257                                      token => $token);
5258                  } else {                  } else {
5259                    !!!cp ('t160');                    !!!cp ('t160');
5260                  }                  }
# Line 4032  sub _tree_construction_main ($) { Line 5266  sub _tree_construction_main ($) {
5266                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5267                                    
5268                  ## reprocess                  ## reprocess
5269                  redo B;                  !!!ack-later;
5270                    next B;
5271                } else {                } else {
5272                  !!!cp ('t161');                  !!!cp ('t161');
5273                  #                  #
# Line 4048  sub _tree_construction_main ($) { Line 5283  sub _tree_construction_main ($) {
5283                  my $i;                  my $i;
5284                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5285                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5286                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5287                      !!!cp ('t163');                      !!!cp ('t163');
5288                      $i = $_;                      $i = $_;
5289                      last INSCOPE;                      last INSCOPE;
5290                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5291                      !!!cp ('t164');                      !!!cp ('t164');
5292                      last INSCOPE;                      last INSCOPE;
5293                    }                    }
5294                  } # INSCOPE                  } # INSCOPE
5295                    unless (defined $i) {                    unless (defined $i) {
5296                      !!!cp ('t165');                      !!!cp ('t165');
5297                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
5298                                        text => $token->{tag_name},
5299                                        token => $token);
5300                      ## Ignore the token                      ## Ignore the token
5301                      !!!next-token;                      !!!next-token;
5302                      redo B;                      next B;
5303                    }                    }
5304                                    
5305                  ## generate implied end tags                  ## generate implied end tags
5306                  while ({                  while ($self->{open_elements}->[-1]->[1]
5307                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5308                    !!!cp ('t166');                    !!!cp ('t166');
5309                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5310                  }                  }
5311    
5312                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5313                            ne $token->{tag_name}) {
5314                    !!!cp ('t167');                    !!!cp ('t167');
5315                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5316                                      text => $self->{open_elements}->[-1]->[0]
5317                                          ->manakai_local_name,
5318                                      token => $token);
5319                  } else {                  } else {
5320                    !!!cp ('t168');                    !!!cp ('t168');
5321                  }                  }
# Line 4089  sub _tree_construction_main ($) { Line 5327  sub _tree_construction_main ($) {
5327                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5328                                    
5329                  !!!next-token;                  !!!next-token;
5330                  redo B;                  next B;
5331                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5332                  !!!cp ('t169');                  !!!cp ('t169');
5333                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5334                                    text => $token->{tag_name}, token => $token);
5335                  ## Ignore the token                  ## Ignore the token
5336                  !!!next-token;                  !!!next-token;
5337                  redo B;                  next B;
5338                } else {                } else {
5339                  !!!cp ('t170');                  !!!cp ('t170');
5340                  #                  #
# Line 4107  sub _tree_construction_main ($) { Line 5346  sub _tree_construction_main ($) {
5346                  INSCOPE: {                  INSCOPE: {
5347                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
5348                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
5349                      if ($node->[1] eq $token->{tag_name}) {                      if ($node->[1] & CAPTION_EL) {
5350                        !!!cp ('t171');                        !!!cp ('t171');
5351                        $i = $_;                        $i = $_;
5352                        last INSCOPE;                        last INSCOPE;
5353                      } elsif ({                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
                               table => 1, html => 1,  
                              }->{$node->[1]}) {  
5354                        !!!cp ('t172');                        !!!cp ('t172');
5355                        last;                        last;
5356                      }                      }
# Line 4121  sub _tree_construction_main ($) { Line 5358  sub _tree_construction_main ($) {
5358    
5359                    !!!cp ('t173');                    !!!cp ('t173');
5360                    !!!parse-error (type => 'unmatched end tag',                    !!!parse-error (type => 'unmatched end tag',
5361                                    value => $token->{tag_name}, token => $token);                                    text => $token->{tag_name}, token => $token);
5362                    ## Ignore the token                    ## Ignore the token
5363                    !!!next-token;                    !!!next-token;
5364                    redo B;                    next B;
5365                  } # INSCOPE                  } # INSCOPE
5366                                    
5367                  ## generate implied end tags                  ## generate implied end tags
5368                  while ({                  while ($self->{open_elements}->[-1]->[1]
5369                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5370                    !!!cp ('t174');                    !!!cp ('t174');
5371                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5372                  }                  }
5373                                    
5374                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5375                    !!!cp ('t175');                    !!!cp ('t175');
5376                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                    !!!parse-error (type => 'not closed',
5377                                      text => $self->{open_elements}->[-1]->[0]
5378                                          ->manakai_local_name,
5379                                      token => $token);
5380                  } else {                  } else {
5381                    !!!cp ('t176');                    !!!cp ('t176');
5382                  }                  }
# Line 4149  sub _tree_construction_main ($) { Line 5388  sub _tree_construction_main ($) {
5388                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5389                                    
5390                  !!!next-token;                  !!!next-token;
5391                  redo B;                  next B;
5392                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5393                  !!!cp ('t177');                  !!!cp ('t177');
5394                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5395                                    text => $token->{tag_name}, token => $token);
5396                  ## Ignore the token                  ## Ignore the token
5397                  !!!next-token;                  !!!next-token;
5398                  redo B;                  next B;
5399                } else {                } else {
5400                  !!!cp ('t178');                  !!!cp ('t178');
5401                  #                  #
# Line 4171  sub _tree_construction_main ($) { Line 5411  sub _tree_construction_main ($) {
5411                INSCOPE: {                INSCOPE: {
5412                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
5413                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5414                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5415                      !!!cp ('t179');                      !!!cp ('t179');
5416                      $i = $_;                      $i = $_;
5417    
5418                      ## Close the cell                      ## Close the cell
5419                      !!!back-token; # </?>                      !!!back-token; # </x>
5420                      $token = {type => END_TAG_TOKEN, tag_name => $tn,                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5421                                line => $token->{line},                                line => $token->{line},
5422                                column => $token->{column}};                                column => $token->{column}};
5423                      redo B;                      next B;
5424                    } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5425                      !!!cp ('t180');                      !!!cp ('t180');
5426                      $tn = $node->[1];                      $tn = $node->[0]->manakai_local_name;
5427                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
5428                      ## in scope in the stack of open elements by definition.                      ## in scope in the stack of open elements by definition.
5429                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5430                      ## ISSUE: Can this be reached?                      ## ISSUE: Can this be reached?
5431                      !!!cp ('t181');                      !!!cp ('t181');
5432                      last;                      last;
# Line 4197  sub _tree_construction_main ($) { Line 5435  sub _tree_construction_main ($) {
5435    
5436                  !!!cp ('t182');                  !!!cp ('t182');
5437                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
5438                      value => $token->{tag_name}, token => $token);                      text => $token->{tag_name}, token => $token);
5439                  ## Ignore the token                  ## Ignore the token
5440                  !!!next-token;                  !!!next-token;
5441                  redo B;                  next B;
5442                } # INSCOPE                } # INSCOPE
5443              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5444                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5445                !!!parse-error (type => 'not closed:caption', token => $token);                !!!parse-error (type => 'not closed', text => 'caption',
5446                                  token => $token);
5447    
5448                ## As if </caption>                ## As if </caption>
5449                ## have a table element in table scope                ## have a table element in table scope
5450                my $i;                my $i;
5451                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5452                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5453                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5454                    !!!cp ('t184');                    !!!cp ('t184');
5455                    $i = $_;                    $i = $_;
5456                    last INSCOPE;                    last INSCOPE;
5457                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5458                    !!!cp ('t185');                    !!!cp ('t185');
5459                    last INSCOPE;                    last INSCOPE;
5460                  }                  }
5461                } # INSCOPE                } # INSCOPE
5462                unless (defined $i) {                unless (defined $i) {
5463                  !!!cp ('t186');                  !!!cp ('t186');
5464                  !!!parse-error (type => 'unmatched end tag:caption', token => $token);                  !!!parse-error (type => 'unmatched end tag',
5465                                    text => 'caption', token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                }                }
5470                                
5471                ## generate implied end tags                ## generate implied end tags
5472                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5473                  !!!cp ('t187');                  !!!cp ('t187');
5474                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5475                }                }
5476    
5477                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5478                  !!!cp ('t188');                  !!!cp ('t188');
5479                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5480                                    text => $self->{open_elements}->[-1]->[0]
5481                                        ->manakai_local_name,
5482                                    token => $token);
5483                } else {                } else {
5484                  !!!cp ('t189');                  !!!cp ('t189');
5485                }                }
# Line 4252  sub _tree_construction_main ($) { Line 5491  sub _tree_construction_main ($) {
5491                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5492    
5493                ## reprocess                ## reprocess
5494                redo B;                next B;
5495              } elsif ({              } elsif ({
5496                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5497                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5498                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5499                  !!!cp ('t190');                  !!!cp ('t190');
5500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5501                                    text => $token->{tag_name}, token => $token);
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  redo B;                  next B;
5505                } else {                } else {
5506                  !!!cp ('t191');                  !!!cp ('t191');
5507                  #                  #
# Line 4272  sub _tree_construction_main ($) { Line 5512  sub _tree_construction_main ($) {
5512                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5513                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5514                !!!cp ('t192');                !!!cp ('t192');
5515                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
5516                                  text => $token->{tag_name}, token => $token);
5517                ## Ignore the token                ## Ignore the token
5518                !!!next-token;                !!!next-token;
5519                redo B;                next B;
5520              } else {              } else {
5521                !!!cp ('t193');                !!!cp ('t193');
5522                #                #
5523              }              }
5524        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5525          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
5526            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]}) {  
5527              !!!cp ('t75');              !!!cp ('t75');
5528              !!!parse-error (type => 'in body:#eof', token => $token);              !!!parse-error (type => 'in body:#eof', token => $token);
5529              last;              last;
# Line 4303  sub _tree_construction_main ($) { Line 5541  sub _tree_construction_main ($) {
5541      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5542        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5543          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5544              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5545            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5546                                
5547            unless (length $token->{data}) {            unless (length $token->{data}) {
5548              !!!cp ('t194');              !!!cp ('t194');
5549              !!!next-token;              !!!next-token;
5550              redo B;              next B;
5551            } else {            } else {
5552              !!!cp ('t195');              !!!cp ('t195');
5553            }            }
5554          }          }
5555    
5556              !!!parse-error (type => 'in table:#character', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
5557    
5558              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5559              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4323  sub _tree_construction_main ($) { Line 5561  sub _tree_construction_main ($) {
5561              ## result in a new Text node.              ## result in a new Text node.
5562              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5563                            
5564              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5565                # MUST                # MUST
5566                my $foster_parent_element;                my $foster_parent_element;
5567                my $next_sibling;                my $next_sibling;
5568                my $prev_sibling;                my $prev_sibling;
5569                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5570                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5571                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5572                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5573                      !!!cp ('t196');                      !!!cp ('t196');
# Line 4367  sub _tree_construction_main ($) { Line 5602  sub _tree_construction_main ($) {
5602          }          }
5603                            
5604          !!!next-token;          !!!next-token;
5605          redo B;          next B;
5606        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5607              if ({          if ({
5608                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5609                   th => 1, td => 1,               th => 1, td => 1,
5610                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5611                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5612                  ## Clear back to table context              ## Clear back to table context
5613                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5614                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5615                    !!!cp ('t201');                !!!cp ('t201');
5616                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5617                  }              }
5618                                
5619                  !!!insert-element ('tbody',, $token);              !!!insert-element ('tbody',, $token);
5620                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5621                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5622                }            }
5623              
5624                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5625                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5626                    !!!cp ('t202');                !!!cp ('t202');
5627                    !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
5628                  }              }
5629                                    
5630                  ## Clear back to table body context              ## Clear back to table body context
5631                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5632                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5633                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5634                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5635                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5636                    pop @{$self->{open_elements}};              }
                 }  
5637                                    
5638                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5639                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5640                    !!!cp ('t204');                    !!!cp ('t204');
5641                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5642                      !!!nack ('t204');
5643                    !!!next-token;                    !!!next-token;
5644                    redo B;                    next B;
5645                  } else {                  } else {
5646                    !!!cp ('t205');                    !!!cp ('t205');
5647                    !!!insert-element ('tr',, $token);                    !!!insert-element ('tr',, $token);
# Line 4417  sub _tree_construction_main ($) { Line 5652  sub _tree_construction_main ($) {
5652                }                }
5653    
5654                ## Clear back to table row context                ## Clear back to table row context
5655                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5656                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5657                  !!!cp ('t207');                  !!!cp ('t207');
5658                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5659                }                }
# Line 4429  sub _tree_construction_main ($) { Line 5663  sub _tree_construction_main ($) {
5663    
5664                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
5665                                
5666                  !!!nack ('t207.1');
5667                !!!next-token;                !!!next-token;
5668                redo B;                next B;
5669              } elsif ({              } elsif ({
5670                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5671                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 4442  sub _tree_construction_main ($) { Line 5677  sub _tree_construction_main ($) {
5677                  my $i;                  my $i;
5678                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5679                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5680                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5681                      !!!cp ('t208');                      !!!cp ('t208');
5682                      $i = $_;                      $i = $_;
5683                      last INSCOPE;                      last INSCOPE;
5684                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
5685                      !!!cp ('t209');                      !!!cp ('t209');
5686                      last INSCOPE;                      last INSCOPE;
5687                    }                    }
5688                  } # INSCOPE                  } # INSCOPE
5689                  unless (defined $i) {                  unless (defined $i) {
5690                   !!!cp ('t210');                    !!!cp ('t210');
5691  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5692                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmacthed end tag',
5693                                      text => $token->{tag_name}, token => $token);
5694                    ## Ignore the token                    ## Ignore the token
5695                      !!!nack ('t210.1');
5696                    !!!next-token;                    !!!next-token;
5697                    redo B;                    next B;
5698                  }                  }
5699                                    
5700                  ## Clear back to table row context                  ## Clear back to table row context
5701                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5702                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5703                    !!!cp ('t211');                    !!!cp ('t211');
5704                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5705                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4479  sub _tree_construction_main ($) { Line 5710  sub _tree_construction_main ($) {
5710                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5711                    !!!cp ('t212');                    !!!cp ('t212');
5712                    ## reprocess                    ## reprocess
5713                    redo B;                    !!!ack-later;
5714                      next B;
5715                  } else {                  } else {
5716                    !!!cp ('t213');                    !!!cp ('t213');
5717                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4491  sub _tree_construction_main ($) { Line 5723  sub _tree_construction_main ($) {
5723                  my $i;                  my $i;
5724                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5725                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5726                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5727                      !!!cp ('t214');                      !!!cp ('t214');
5728                      $i = $_;                      $i = $_;
5729                      last INSCOPE;                      last INSCOPE;
5730                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5731                      !!!cp ('t215');                      !!!cp ('t215');
5732                      last INSCOPE;                      last INSCOPE;
5733                    }                    }
5734                  } # INSCOPE                  } # INSCOPE
5735                  unless (defined $i) {                  unless (defined $i) {
5736                    !!!cp ('t216');                    !!!cp ('t216');
5737  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5738                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5739                                      text => $token->{tag_name}, token => $token);
5740                    ## Ignore the token                    ## Ignore the token
5741                      !!!nack ('t216.1');
5742                    !!!next-token;                    !!!next-token;
5743                    redo B;                    next B;
5744                  }                  }
5745    
5746                  ## Clear back to table body context                  ## Clear back to table body context
5747                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5748                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5749                    !!!cp ('t217');                    !!!cp ('t217');
5750                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5751                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4538  sub _tree_construction_main ($) { Line 5767  sub _tree_construction_main ($) {
5767    
5768                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
5769                  ## Clear back to table context                  ## Clear back to table context
5770                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5771                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5772                    !!!cp ('t219');                    !!!cp ('t219');
5773                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5774                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4548  sub _tree_construction_main ($) { Line 5777  sub _tree_construction_main ($) {
5777                  !!!insert-element ('colgroup',, $token);                  !!!insert-element ('colgroup',, $token);
5778                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5779                  ## reprocess                  ## reprocess
5780                  redo B;                  !!!ack-later;
5781                    next B;
5782                } elsif ({                } elsif ({
5783                          caption => 1,                          caption => 1,
5784                          colgroup => 1,                          colgroup => 1,
5785                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
5786                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
5787                  ## Clear back to table context                  ## Clear back to table context
5788                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5789                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5790                    !!!cp ('t220');                    !!!cp ('t220');
5791                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5792                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4574  sub _tree_construction_main ($) { Line 5804  sub _tree_construction_main ($) {
5804                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
5805                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
5806                  !!!next-token;                  !!!next-token;
5807                  redo B;                  !!!nack ('t220.1');
5808                    next B;
5809                } else {                } else {
5810                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
5811                }                }
5812              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5813                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
5814                                  text => $self->{open_elements}->[-1]->[0]
5815                                      ->manakai_local_name,
5816                                  token => $token);
5817    
5818                ## As if </table>                ## As if </table>
5819                ## have a table element in table scope                ## have a table element in table scope
5820                my $i;                my $i;
5821                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5822                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5823                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5824                    !!!cp ('t221');                    !!!cp ('t221');
5825                    $i = $_;                    $i = $_;
5826                    last INSCOPE;                    last INSCOPE;
5827                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5828                    !!!cp ('t222');                    !!!cp ('t222');
5829                    last INSCOPE;                    last INSCOPE;
5830                  }                  }
# Line 4601  sub _tree_construction_main ($) { Line 5832  sub _tree_construction_main ($) {
5832                unless (defined $i) {                unless (defined $i) {
5833                  !!!cp ('t223');                  !!!cp ('t223');
5834  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5835                  !!!parse-error (type => 'unmatched end tag:table', token => $token);                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5836                                    token => $token);
5837                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5838                    !!!nack ('t223.1');
5839                  !!!next-token;                  !!!next-token;
5840                  redo B;                  next B;
5841                }                }
5842                                
5843  ## TODO: Followings are removed from the latest spec.  ## TODO: Followings are removed from the latest spec.
5844                ## generate implied end tags                ## generate implied end tags
5845                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5846                  !!!cp ('t224');                  !!!cp ('t224');
5847                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5848                }                }
5849    
5850                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5851                  !!!cp ('t225');                  !!!cp ('t225');
5852  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5853                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                  !!!parse-error (type => 'not closed',
5854                                    text => $self->{open_elements}->[-1]->[0]
5855                                        ->manakai_local_name,
5856                                    token => $token);
5857                } else {                } else {
5858                  !!!cp ('t226');                  !!!cp ('t226');
5859                }                }
# Line 4629  sub _tree_construction_main ($) { Line 5863  sub _tree_construction_main ($) {
5863    
5864                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5865    
5866                ## reprocess            ## reprocess
5867                redo B;            !!!ack-later;
5868              next B;
5869          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5870            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5871              !!!cp ('t227.8');              !!!cp ('t227.8');
5872              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5873              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5874              redo B;              next B;
5875            } else {            } else {
5876              !!!cp ('t227.7');              !!!cp ('t227.7');
5877              #              #
# Line 4646  sub _tree_construction_main ($) { Line 5881  sub _tree_construction_main ($) {
5881              !!!cp ('t227.6');              !!!cp ('t227.6');
5882              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5883              $script_start_tag->();              $script_start_tag->();
5884              redo B;              next B;
5885            } else {            } else {
5886              !!!cp ('t227.5');              !!!cp ('t227.5');
5887              #              #
# Line 4657  sub _tree_construction_main ($) { Line 5892  sub _tree_construction_main ($) {
5892                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5893                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5894                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5895                  !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'in table',
5896                                    text => $token->{tag_name}, token => $token);
5897    
5898                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5899    
# Line 4666  sub _tree_construction_main ($) { Line 5902  sub _tree_construction_main ($) {
5902                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5903    
5904                  !!!next-token;                  !!!next-token;
5905                  redo B;                  !!!ack ('t227.2.1');
5906                    next B;
5907                } else {                } else {
5908                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5909                  #                  #
# Line 4684  sub _tree_construction_main ($) { Line 5921  sub _tree_construction_main ($) {
5921            #            #
5922          }          }
5923    
5924          !!!parse-error (type => 'in table:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in table', text => $token->{tag_name},
5925                            token => $token);
5926    
5927          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5928          #          #
# Line 4695  sub _tree_construction_main ($) { Line 5933  sub _tree_construction_main ($) {
5933                my $i;                my $i;
5934                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5935                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5936                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5937                    !!!cp ('t228');                    !!!cp ('t228');
5938                    $i = $_;                    $i = $_;
5939                    last INSCOPE;                    last INSCOPE;
5940                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5941                    !!!cp ('t229');                    !!!cp ('t229');
5942                    last INSCOPE;                    last INSCOPE;
5943                  }                  }
5944                } # INSCOPE                } # INSCOPE
5945                unless (defined $i) {                unless (defined $i) {
5946                  !!!cp ('t230');                  !!!cp ('t230');
5947                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
5948                                    text => $token->{tag_name}, token => $token);
5949                  ## Ignore the token                  ## Ignore the token
5950                    !!!nack ('t230.1');
5951                  !!!next-token;                  !!!next-token;
5952                  redo B;                  next B;
5953                } else {                } else {
5954                  !!!cp ('t232');                  !!!cp ('t232');
5955                }                }
5956    
5957                ## Clear back to table row context                ## Clear back to table row context
5958                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5959                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5960                  !!!cp ('t231');                  !!!cp ('t231');
5961  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5962                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4728  sub _tree_construction_main ($) { Line 5965  sub _tree_construction_main ($) {
5965                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5966                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5967                !!!next-token;                !!!next-token;
5968                redo B;                !!!nack ('t231.1');
5969                  next B;
5970              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5971                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5972                  ## As if </tr>                  ## As if </tr>
# Line 4736  sub _tree_construction_main ($) { Line 5974  sub _tree_construction_main ($) {
5974                  my $i;                  my $i;
5975                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5976                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5977                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5978                      !!!cp ('t233');                      !!!cp ('t233');
5979                      $i = $_;                      $i = $_;
5980                      last INSCOPE;                      last INSCOPE;
5981                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5982                      !!!cp ('t234');                      !!!cp ('t234');
5983                      last INSCOPE;                      last INSCOPE;
5984                    }                    }
# Line 4750  sub _tree_construction_main ($) { Line 5986  sub _tree_construction_main ($) {
5986                  unless (defined $i) {                  unless (defined $i) {
5987                    !!!cp ('t235');                    !!!cp ('t235');
5988  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5989                    !!!parse-error (type => 'unmatched end tag:'.$token->{type}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
5990                                      text => $token->{type}, token => $token);
5991                    ## Ignore the token                    ## Ignore the token
5992                      !!!nack ('t236.1');
5993                    !!!next-token;                    !!!next-token;
5994                    redo B;                    next B;
5995                  }                  }
5996                                    
5997                  ## Clear back to table row context                  ## Clear back to table row context
5998                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5999                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6000                    !!!cp ('t236');                    !!!cp ('t236');
6001  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6002                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4775  sub _tree_construction_main ($) { Line 6012  sub _tree_construction_main ($) {
6012                  my $i;                  my $i;
6013                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6014                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6015                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6016                      !!!cp ('t237');                      !!!cp ('t237');
6017                      $i = $_;                      $i = $_;
6018                      last INSCOPE;                      last INSCOPE;
6019                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6020                      !!!cp ('t238');                      !!!cp ('t238');
6021                      last INSCOPE;                      last INSCOPE;
6022                    }                    }
6023                  } # INSCOPE                  } # INSCOPE
6024                  unless (defined $i) {                  unless (defined $i) {
6025                    !!!cp ('t239');                    !!!cp ('t239');
6026                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                    !!!parse-error (type => 'unmatched end tag',
6027                                      text => $token->{tag_name}, token => $token);
6028                    ## Ignore the token                    ## Ignore the token
6029                      !!!nack ('t239.1');
6030                    !!!next-token;                    !!!next-token;
6031                    redo B;                    next B;
6032                  }                  }
6033                                    
6034                  ## Clear back to table body context                  ## Clear back to table body context
6035                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6036                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6037                    !!!cp ('t240');                    !!!cp ('t240');
6038                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6039                  }                  }
# Line 4825  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 ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6063                    !!!cp ('t241');                    !!!cp ('t241');
6064                    $i = $_;                    $i = $_;
6065                    last INSCOPE;                    last INSCOPE;
6066                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6067                    !!!cp ('t242');                    !!!cp ('t242');
6068                    last INSCOPE;                    last INSCOPE;
6069                  }                  }
6070                } # INSCOPE                } # INSCOPE
6071                unless (defined $i) {                unless (defined $i) {
6072                  !!!cp ('t243');                  !!!cp ('t243');
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 ('t243.1');
6077                  !!!next-token;                  !!!next-token;
6078                  redo B;                  next B;
6079                }                }
6080                                    
6081                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4850  sub _tree_construction_main ($) { Line 6084  sub _tree_construction_main ($) {
6084                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6085                                
6086                !!!next-token;                !!!next-token;
6087                redo B;                next B;
6088              } elsif ({              } elsif ({
6089                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6090                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4860  sub _tree_construction_main ($) { Line 6094  sub _tree_construction_main ($) {
6094                  my $i;                  my $i;
6095                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6096                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6097                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6098                      !!!cp ('t247');                      !!!cp ('t247');
6099                      $i = $_;                      $i = $_;
6100                      last INSCOPE;                      last INSCOPE;
6101                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6102                      !!!cp ('t248');                      !!!cp ('t248');
6103                      last INSCOPE;                      last INSCOPE;
6104                    }                    }
6105                  } # INSCOPE                  } # INSCOPE
6106                    unless (defined $i) {                    unless (defined $i) {
6107                      !!!cp ('t249');                      !!!cp ('t249');
6108                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                      !!!parse-error (type => 'unmatched end tag',
6109                                        text => $token->{tag_name}, token => $token);
6110                      ## Ignore the token                      ## Ignore the token
6111                        !!!nack ('t249.1');
6112                      !!!next-token;                      !!!next-token;
6113                      redo B;                      next B;
6114                    }                    }
6115                                    
6116                  ## As if </tr>                  ## As if </tr>
# Line 4884  sub _tree_construction_main ($) { Line 6118  sub _tree_construction_main ($) {
6118                  my $i;                  my $i;
6119                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6120                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6121                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6122                      !!!cp ('t250');                      !!!cp ('t250');
6123                      $i = $_;                      $i = $_;
6124                      last INSCOPE;                      last INSCOPE;
6125                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6126                      !!!cp ('t251');                      !!!cp ('t251');
6127                      last INSCOPE;                      last INSCOPE;
6128                    }                    }
6129                  } # INSCOPE                  } # INSCOPE
6130                    unless (defined $i) {                    unless (defined $i) {
6131                      !!!cp ('t252');                      !!!cp ('t252');
6132                      !!!parse-error (type => 'unmatched end tag:tr', token => $token);                      !!!parse-error (type => 'unmatched end tag',
6133                                        text => 'tr', token => $token);
6134                      ## Ignore the token                      ## Ignore the token
6135                        !!!nack ('t252.1');
6136                      !!!next-token;                      !!!next-token;
6137                      redo B;                      next B;
6138                    }                    }
6139                                    
6140                  ## Clear back to table row context                  ## Clear back to table row context
6141                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6142                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6143                    !!!cp ('t253');                    !!!cp ('t253');
6144  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6145                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4921  sub _tree_construction_main ($) { Line 6154  sub _tree_construction_main ($) {
6154                my $i;                my $i;
6155                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6156                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6157                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6158                    !!!cp ('t254');                    !!!cp ('t254');
6159                    $i = $_;                    $i = $_;
6160                    last INSCOPE;                    last INSCOPE;
6161                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6162                    !!!cp ('t255');                    !!!cp ('t255');
6163                    last INSCOPE;                    last INSCOPE;
6164                  }                  }
6165                } # INSCOPE                } # INSCOPE
6166                unless (defined $i) {                unless (defined $i) {
6167                  !!!cp ('t256');                  !!!cp ('t256');
6168                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                  !!!parse-error (type => 'unmatched end tag',
6169                                    text => $token->{tag_name}, token => $token);
6170                  ## Ignore the token                  ## Ignore the token
6171                    !!!nack ('t256.1');
6172                  !!!next-token;                  !!!next-token;
6173                  redo B;                  next B;
6174                }                }
6175    
6176                ## Clear back to table body context                ## Clear back to table body context
6177                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6178                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6179                  !!!cp ('t257');                  !!!cp ('t257');
6180  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6181                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4951  sub _tree_construction_main ($) { Line 6183  sub _tree_construction_main ($) {
6183    
6184                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6185                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6186                  !!!nack ('t257.1');
6187                !!!next-token;                !!!next-token;
6188                redo B;                next B;
6189              } elsif ({              } elsif ({
6190                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6191                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6192                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6193                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6194                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6195                !!!cp ('t258');            !!!cp ('t258');
6196                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6197                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6198                !!!next-token;            ## Ignore the token
6199                redo B;            !!!nack ('t258.1');
6200               !!!next-token;
6201              next B;
6202          } else {          } else {
6203            !!!cp ('t259');            !!!cp ('t259');
6204            !!!parse-error (type => 'in table:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in table:/',
6205                              text => $token->{tag_name}, token => $token);
6206    
6207            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6208            #            #
6209          }          }
6210        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6211          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6212                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6213            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
6214            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 4989  sub _tree_construction_main ($) { Line 6225  sub _tree_construction_main ($) {
6225        }        }
6226      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6227            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6228              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6229                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6230                unless (length $token->{data}) {                unless (length $token->{data}) {
6231                  !!!cp ('t260');                  !!!cp ('t260');
6232                  !!!next-token;                  !!!next-token;
6233                  redo B;                  next B;
6234                }                }
6235              }              }
6236                            
# Line 5005  sub _tree_construction_main ($) { Line 6241  sub _tree_construction_main ($) {
6241                !!!cp ('t262');                !!!cp ('t262');
6242                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6243                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6244                  !!!ack ('t262.1');
6245                !!!next-token;                !!!next-token;
6246                redo B;                next B;
6247              } else {              } else {
6248                !!!cp ('t263');                !!!cp ('t263');
6249                #                #
6250              }              }
6251            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6252              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6253                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6254                  !!!cp ('t264');                  !!!cp ('t264');
6255                  !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);                  !!!parse-error (type => 'unmatched end tag',
6256                                    text => 'colgroup', token => $token);
6257                  ## Ignore the token                  ## Ignore the token
6258                  !!!next-token;                  !!!next-token;
6259                  redo B;                  next B;
6260                } else {                } else {
6261                  !!!cp ('t265');                  !!!cp ('t265');
6262                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6263                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6264                  !!!next-token;                  !!!next-token;
6265                  redo B;                              next B;            
6266                }                }
6267              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6268                !!!cp ('t266');                !!!cp ('t266');
6269                !!!parse-error (type => 'unmatched end tag:col', token => $token);                !!!parse-error (type => 'unmatched end tag',
6270                                  text => 'col', token => $token);
6271                ## Ignore the token                ## Ignore the token
6272                !!!next-token;                !!!next-token;
6273                redo B;                next B;
6274              } else {              } else {
6275                !!!cp ('t267');                !!!cp ('t267');
6276                #                #
6277              }              }
6278        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6279          if ($self->{open_elements}->[-1]->[1] eq 'html' or          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6280              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
6281            !!!cp ('t270.2');            !!!cp ('t270.2');
6282            ## Stop parsing.            ## Stop parsing.
# Line 5048  sub _tree_construction_main ($) { Line 6287  sub _tree_construction_main ($) {
6287            pop @{$self->{open_elements}}; # colgroup            pop @{$self->{open_elements}}; # colgroup
6288            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6289            ## Reprocess.            ## Reprocess.
6290            redo B;            next B;
6291          }          }
6292        } else {        } else {
6293          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6294        }        }
6295    
6296            ## As if </colgroup>            ## As if </colgroup>
6297            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6298              !!!cp ('t269');              !!!cp ('t269');
6299  ## TODO: Wrong error type?  ## TODO: Wrong error type?
6300              !!!parse-error (type => 'unmatched end tag:colgroup', token => $token);              !!!parse-error (type => 'unmatched end tag',
6301                                text => 'colgroup', token => $token);
6302              ## Ignore the token              ## Ignore the token
6303                !!!nack ('t269.1');
6304              !!!next-token;              !!!next-token;
6305              redo B;              next B;
6306            } else {            } else {
6307              !!!cp ('t270');              !!!cp ('t270');
6308              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6309              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6310                !!!ack-later;
6311              ## reprocess              ## reprocess
6312              redo B;              next B;
6313            }            }
6314      } elsif ($self->{insertion_mode} & SELECT_IMS) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6315        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6316          !!!cp ('t271');          !!!cp ('t271');
6317          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6318          !!!next-token;          !!!next-token;
6319          redo B;          next B;
6320        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6321              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6322                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6323                  !!!cp ('t272');              !!!cp ('t272');
6324                  ## As if </option>              ## As if </option>
6325                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6326                } else {            } else {
6327                  !!!cp ('t273');              !!!cp ('t273');
6328                }            }
6329    
6330                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6331                !!!next-token;            !!!nack ('t273.1');
6332                redo B;            !!!next-token;
6333              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6334                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6335                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6336                  ## As if </option>              !!!cp ('t274');
6337                  pop @{$self->{open_elements}};              ## As if </option>
6338                } else {              pop @{$self->{open_elements}};
6339                  !!!cp ('t275');            } else {
6340                }              !!!cp ('t275');
6341              }
6342    
6343                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6344                  !!!cp ('t276');              !!!cp ('t276');
6345                  ## As if </optgroup>              ## As if </optgroup>
6346                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6347                } else {            } else {
6348                  !!!cp ('t277');              !!!cp ('t277');
6349                }            }
6350    
6351                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6352                !!!next-token;            !!!nack ('t277.1');
6353                redo B;            !!!next-token;
6354          } elsif ($token->{tag_name} eq 'select' or            next B;
6355                   $token->{tag_name} eq 'input' or          } elsif ({
6356                       select => 1, input => 1, textarea => 1,
6357                     }->{$token->{tag_name}} or
6358                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6359                    {                    {
6360                     caption => 1, table => 1,                     caption => 1, table => 1,
# Line 5117  sub _tree_construction_main ($) { Line 6362  sub _tree_construction_main ($) {
6362                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
6363                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
6364            ## TODO: The type below is not good - <select> is replaced by </select>            ## TODO: The type below is not good - <select> is replaced by </select>
6365            !!!parse-error (type => 'not closed:select', token => $token);            !!!parse-error (type => 'not closed', text => 'select',
6366                              token => $token);
6367            ## NOTE: As if the token were </select> (<select> case) or            ## NOTE: As if the token were </select> (<select> case) or
6368            ## as if there were </select> (otherwise).            ## as if there were </select> (otherwise).
6369                ## have an element in table scope            ## have an element in table scope
6370                my $i;            my $i;
6371                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6372                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6373                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6374                    !!!cp ('t278');                !!!cp ('t278');
6375                    $i = $_;                $i = $_;
6376                    last INSCOPE;                last INSCOPE;
6377                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6378                            table => 1, html => 1,                !!!cp ('t279');
6379                           }->{$node->[1]}) {                last INSCOPE;
6380                    !!!cp ('t279');              }
6381                    last INSCOPE;            } # INSCOPE
6382                  }            unless (defined $i) {
6383                } # INSCOPE              !!!cp ('t280');
6384                unless (defined $i) {              !!!parse-error (type => 'unmatched end tag',
6385                  !!!cp ('t280');                              text => 'select', token => $token);
6386                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              ## Ignore the token
6387                  ## Ignore the token              !!!nack ('t280.1');
6388                  !!!next-token;              !!!next-token;
6389                  redo B;              next B;
6390                }            }
6391                                
6392                !!!cp ('t281');            !!!cp ('t281');
6393                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6394    
6395                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6396    
6397            if ($token->{tag_name} eq 'select') {            if ($token->{tag_name} eq 'select') {
6398              !!!cp ('t281.2');              !!!nack ('t281.2');
6399              !!!next-token;              !!!next-token;
6400              redo B;              next B;
6401            } else {            } else {
6402              !!!cp ('t281.1');              !!!cp ('t281.1');
6403                !!!ack-later;
6404              ## Reprocess the token.              ## Reprocess the token.
6405              redo B;              next B;
6406            }            }
6407          } else {          } else {
6408            !!!cp ('t282');            !!!cp ('t282');
6409            !!!parse-error (type => 'in select:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select',
6410                              text => $token->{tag_name}, token => $token);
6411            ## Ignore the token            ## Ignore the token
6412              !!!nack ('t282.1');
6413            !!!next-token;            !!!next-token;
6414            redo B;            next B;
6415          }          }
6416        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6417              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6418                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6419                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6420                  !!!cp ('t283');              !!!cp ('t283');
6421                  ## As if </option>              ## As if </option>
6422                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6423                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6424                  !!!cp ('t284');              !!!cp ('t284');
6425                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6426                } else {            } else {
6427                  !!!cp ('t285');              !!!cp ('t285');
6428                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6429                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6430                }              ## Ignore the token
6431                !!!next-token;            }
6432                redo B;            !!!nack ('t285.1');
6433              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6434                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6435                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6436                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6437                } else {              !!!cp ('t286');
6438                  !!!cp ('t287');              pop @{$self->{open_elements}};
6439                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            } else {
6440                  ## Ignore the token              !!!cp ('t287');
6441                }              !!!parse-error (type => 'unmatched end tag',
6442                !!!next-token;                              text => $token->{tag_name}, token => $token);
6443                redo B;              ## Ignore the token
6444              } elsif ($token->{tag_name} eq 'select') {            }
6445                ## have an element in table scope            !!!nack ('t287.1');
6446                my $i;            !!!next-token;
6447                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6448                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6449                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6450                    !!!cp ('t288');            my $i;
6451                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6452                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6453                  } elsif ({              if ($node->[1] & SELECT_EL) {
6454                            table => 1, html => 1,                !!!cp ('t288');
6455                           }->{$node->[1]}) {                $i = $_;
6456                    !!!cp ('t289');                last INSCOPE;
6457                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6458                  }                !!!cp ('t289');
6459                } # INSCOPE                last INSCOPE;
6460                unless (defined $i) {              }
6461                  !!!cp ('t290');            } # INSCOPE
6462                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            unless (defined $i) {
6463                  ## Ignore the token              !!!cp ('t290');
6464                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6465                  redo B;                              text => $token->{tag_name}, token => $token);
6466                }              ## Ignore the token
6467                !!!nack ('t290.1');
6468                !!!next-token;
6469                next B;
6470              }
6471                                
6472                !!!cp ('t291');            !!!cp ('t291');
6473                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6474    
6475                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6476    
6477                !!!next-token;            !!!nack ('t291.1');
6478                redo B;            !!!next-token;
6479              next B;
6480          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6481                   {                   {
6482                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
6483                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6484                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
6485  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6486                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
6487                              text => $token->{tag_name}, token => $token);
6488                                
6489                ## have an element in table scope            ## have an element in table scope
6490                my $i;            my $i;
6491                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6492                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6493                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6494                    !!!cp ('t292');                !!!cp ('t292');
6495                    $i = $_;                $i = $_;
6496                    last INSCOPE;                last INSCOPE;
6497                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6498                            table => 1, html => 1,                !!!cp ('t293');
6499                           }->{$node->[1]}) {                last INSCOPE;
6500                    !!!cp ('t293');              }
6501                    last INSCOPE;            } # INSCOPE
6502                  }            unless (defined $i) {
6503                } # INSCOPE              !!!cp ('t294');
6504                unless (defined $i) {              ## Ignore the token
6505                  !!!cp ('t294');              !!!nack ('t294.1');
6506                  ## Ignore the token              !!!next-token;
6507                  !!!next-token;              next B;
6508                  redo B;            }
               }  
6509                                
6510                ## As if </select>            ## As if </select>
6511                ## have an element in table scope            ## have an element in table scope
6512                undef $i;            undef $i;
6513                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6514                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6515                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6516                    !!!cp ('t295');                !!!cp ('t295');
6517                    $i = $_;                $i = $_;
6518                    last INSCOPE;                last INSCOPE;
6519                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6520  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6521                    !!!cp ('t296');                !!!cp ('t296');
6522                    last INSCOPE;                last INSCOPE;
6523                  }              }
6524                } # INSCOPE            } # INSCOPE
6525                unless (defined $i) {            unless (defined $i) {
6526                  !!!cp ('t297');              !!!cp ('t297');
6527  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6528                  !!!parse-error (type => 'unmatched end tag:select', token => $token);              !!!parse-error (type => 'unmatched end tag',
6529                  ## Ignore the </select> token                              text => 'select', token => $token);
6530                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6531                  redo B;              !!!nack ('t297.1');
6532                }              !!!next-token; ## TODO: ok?
6533                next B;
6534              }
6535                                
6536                !!!cp ('t298');            !!!cp ('t298');
6537                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6538    
6539                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6540    
6541                ## reprocess            !!!ack-later;
6542                redo B;            ## reprocess
6543              next B;
6544          } else {          } else {
6545            !!!cp ('t299');            !!!cp ('t299');
6546            !!!parse-error (type => 'in select:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'in select:/',
6547                              text => $token->{tag_name}, token => $token);
6548            ## Ignore the token            ## Ignore the token
6549              !!!nack ('t299.3');
6550            !!!next-token;            !!!next-token;
6551            redo B;            next B;
6552          }          }
6553        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6554          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6555                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6556            !!!cp ('t299.1');            !!!cp ('t299.1');
6557            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5309  sub _tree_construction_main ($) { Line 6566  sub _tree_construction_main ($) {
6566        }        }
6567      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6568        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6569          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6570            my $data = $1;            my $data = $1;
6571            ## As if in body            ## As if in body
6572            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5319  sub _tree_construction_main ($) { Line 6576  sub _tree_construction_main ($) {
6576            unless (length $token->{data}) {            unless (length $token->{data}) {
6577              !!!cp ('t300');              !!!cp ('t300');
6578              !!!next-token;              !!!next-token;
6579              redo B;              next B;
6580            }            }
6581          }          }
6582                    
6583          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6584            !!!cp ('t301');            !!!cp ('t301');
6585            !!!parse-error (type => 'after html:#character', token => $token);            !!!parse-error (type => 'after html:#text', token => $token);
6586              #
           ## Reprocess in the "after body" insertion mode.  
6587          } else {          } else {
6588            !!!cp ('t302');            !!!cp ('t302');
6589              ## "after body" insertion mode
6590              !!!parse-error (type => 'after body:#text', token => $token);
6591              #
6592          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character', token => $token);  
6593    
6594          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6595          ## reprocess          ## reprocess
6596          redo B;          next B;
6597        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6598          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6599            !!!cp ('t303');            !!!cp ('t303');
6600            !!!parse-error (type => 'after html:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html',
6601                                        text => $token->{tag_name}, token => $token);
6602            ## Reprocess in the "after body" insertion mode.            #
6603          } else {          } else {
6604            !!!cp ('t304');            !!!cp ('t304');
6605              ## "after body" insertion mode
6606              !!!parse-error (type => 'after body',
6607                              text => $token->{tag_name}, token => $token);
6608              #
6609          }          }
6610    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name}, token => $token);  
   
6611          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6612            !!!ack-later;
6613          ## reprocess          ## reprocess
6614          redo B;          next B;
6615        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6616          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6617            !!!cp ('t305');            !!!cp ('t305');
6618            !!!parse-error (type => 'after html:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after html:/',
6619                              text => $token->{tag_name}, token => $token);
6620                        
6621            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6622            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6623              next B;
6624          } else {          } else {
6625            !!!cp ('t306');            !!!cp ('t306');
6626          }          }
# Line 5369  sub _tree_construction_main ($) { Line 6629  sub _tree_construction_main ($) {
6629          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6630            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6631              !!!cp ('t307');              !!!cp ('t307');
6632              !!!parse-error (type => 'unmatched end tag:html', token => $token);              !!!parse-error (type => 'unmatched end tag',
6633                                text => 'html', token => $token);
6634              ## Ignore the token              ## Ignore the token
6635              !!!next-token;              !!!next-token;
6636              redo B;              next B;
6637            } else {            } else {
6638              !!!cp ('t308');              !!!cp ('t308');
6639              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6640              !!!next-token;              !!!next-token;
6641              redo B;              next B;
6642            }            }
6643          } else {          } else {
6644            !!!cp ('t309');            !!!cp ('t309');
6645            !!!parse-error (type => 'after body:/'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'after body:/',
6646                              text => $token->{tag_name}, token => $token);
6647    
6648            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6649            ## reprocess            ## reprocess
6650            redo B;            next B;
6651          }          }
6652        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6653          !!!cp ('t309.2');          !!!cp ('t309.2');
# Line 5396  sub _tree_construction_main ($) { Line 6658  sub _tree_construction_main ($) {
6658        }        }
6659      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6660        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6661          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6662            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6663                        
6664            unless (length $token->{data}) {            unless (length $token->{data}) {
6665              !!!cp ('t310');              !!!cp ('t310');
6666              !!!next-token;              !!!next-token;
6667              redo B;              next B;
6668            }            }
6669          }          }
6670                    
6671          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6672            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6673              !!!cp ('t311');              !!!cp ('t311');
6674              !!!parse-error (type => 'in frameset:#character', token => $token);              !!!parse-error (type => 'in frameset:#text', token => $token);
6675            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6676              !!!cp ('t312');              !!!cp ('t312');
6677              !!!parse-error (type => 'after frameset:#character', token => $token);              !!!parse-error (type => 'after frameset:#text', token => $token);
6678            } else { # "after html frameset"            } else { # "after after frameset"
6679              !!!cp ('t313');              !!!cp ('t313');
6680              !!!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);  
6681            }            }
6682                        
6683            ## Ignore the token.            ## Ignore the token.
# Line 5430  sub _tree_construction_main ($) { Line 6688  sub _tree_construction_main ($) {
6688              !!!cp ('t315');              !!!cp ('t315');
6689              !!!next-token;              !!!next-token;
6690            }            }
6691            redo B;            next B;
6692          }          }
6693                    
6694          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6695        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6696          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6697              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6698            !!!cp ('t318');            !!!cp ('t318');
6699            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6700              !!!nack ('t318.1');
6701            !!!next-token;            !!!next-token;
6702            redo B;            next B;
6703          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6704                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6705            !!!cp ('t319');            !!!cp ('t319');
6706            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6707            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6708              !!!ack ('t319.1');
6709            !!!next-token;            !!!next-token;
6710            redo B;            next B;
6711          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6712            !!!cp ('t320');            !!!cp ('t320');
6713            ## NOTE: As if in body.            ## NOTE: As if in head.
6714            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6715            redo B;            next B;
6716    
6717              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6718              ## has no parse error.
6719          } else {          } else {
6720            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721              !!!cp ('t321');              !!!cp ('t321');
6722              !!!parse-error (type => 'in frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset',
6723            } else {                              text => $token->{tag_name}, token => $token);
6724              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6725              !!!cp ('t322');              !!!cp ('t322');
6726              !!!parse-error (type => 'after frameset:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after frameset',
6727                                text => $token->{tag_name}, token => $token);
6728              } else { # "after after frameset"
6729                !!!cp ('t322.2');
6730                !!!parse-error (type => 'after after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732            }            }
6733            ## Ignore the token            ## Ignore the token
6734              !!!nack ('t322.1');
6735            !!!next-token;            !!!next-token;
6736            redo B;            next B;
6737          }          }
6738        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!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');  
         }  
   
6739          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6740              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6741            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6742                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6743              !!!cp ('t325');              !!!cp ('t325');
6744              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
6745                                text => $token->{tag_name}, token => $token);
6746              ## Ignore the token              ## Ignore the token
6747              !!!next-token;              !!!next-token;
6748            } else {            } else {
# Line 5501  sub _tree_construction_main ($) { Line 6752  sub _tree_construction_main ($) {
6752            }            }
6753    
6754            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6755                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6756              !!!cp ('t327');              !!!cp ('t327');
6757              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6758            } else {            } else {
6759              !!!cp ('t328');              !!!cp ('t328');
6760            }            }
6761            redo B;            next B;
6762          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6763                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6764            !!!cp ('t329');            !!!cp ('t329');
6765            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6766            !!!next-token;            !!!next-token;
6767            redo B;            next B;
6768          } else {          } else {
6769            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6770              !!!cp ('t330');              !!!cp ('t330');
6771              !!!parse-error (type => 'in frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'in frameset:/',
6772            } else {                              text => $token->{tag_name}, token => $token);
6773              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6774                !!!cp ('t330.1');
6775                !!!parse-error (type => 'after frameset:/',
6776                                text => $token->{tag_name}, token => $token);
6777              } else { # "after after html"
6778              !!!cp ('t331');              !!!cp ('t331');
6779              !!!parse-error (type => 'after frameset:/'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'after after frameset:/',
6780                                text => $token->{tag_name}, token => $token);
6781            }            }
6782            ## Ignore the token            ## Ignore the token
6783            !!!next-token;            !!!next-token;
6784            redo B;            next B;
6785          }          }
6786        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6787          unless ($self->{open_elements}->[-1]->[1] eq 'html' and          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6788                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
6789            !!!cp ('t331.1');            !!!cp ('t331.1');
6790            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 5540  sub _tree_construction_main ($) { Line 6797  sub _tree_construction_main ($) {
6797        } else {        } else {
6798          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6799        }        }
   
       ## ISSUE: An issue in spec here  
6800      } else {      } else {
6801        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6802      }      }
# Line 5552  sub _tree_construction_main ($) { Line 6807  sub _tree_construction_main ($) {
6807          !!!cp ('t332');          !!!cp ('t332');
6808          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6809          $script_start_tag->();          $script_start_tag->();
6810          redo B;          next B;
6811        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6812          !!!cp ('t333');          !!!cp ('t333');
6813          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6814          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6815          redo B;          next B;
6816        } elsif ({        } elsif ({
6817                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6818                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6819          !!!cp ('t334');          !!!cp ('t334');
6820          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6821          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6822          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6823            !!!ack ('t334.1');
6824          !!!next-token;          !!!next-token;
6825          redo B;          next B;
6826        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6827          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6828          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6829          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6830    
6831          unless ($self->{confident}) {          unless ($self->{confident}) {
6832            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6833              !!!cp ('t335');              !!!cp ('t335');
6834                ## NOTE: Whether the encoding is supported or not is handled
6835                ## in the {change_encoding} callback.
6836              $self->{change_encoding}              $self->{change_encoding}
6837                  ->($self, $token->{attributes}->{charset}->{value}, $token);                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6838                            
# Line 5583  sub _tree_construction_main ($) { Line 6841  sub _tree_construction_main ($) {
6841                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6842                                           ->{has_reference});                                           ->{has_reference});
6843            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6844              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6845                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6846                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6847                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6848                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6849                       /x) {
6850                !!!cp ('t336');                !!!cp ('t336');
6851                  ## NOTE: Whether the encoding is supported or not is handled
6852                  ## in the {change_encoding} callback.
6853                $self->{change_encoding}                $self->{change_encoding}
6854                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6855                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
# Line 5615  sub _tree_construction_main ($) { Line 6875  sub _tree_construction_main ($) {
6875            }            }
6876          }          }
6877    
6878            !!!ack ('t338.1');
6879          !!!next-token;          !!!next-token;
6880          redo B;          next B;
6881        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6882          !!!cp ('t341');          !!!cp ('t341');
6883          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6884          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6885          redo B;          next B;
6886        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6887          !!!parse-error (type => 'in body:body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
6888                                
6889          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6890              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6891            !!!cp ('t342');            !!!cp ('t342');
6892            ## Ignore the token            ## Ignore the token
6893          } else {          } else {
# Line 5640  sub _tree_construction_main ($) { Line 6901  sub _tree_construction_main ($) {
6901              }              }
6902            }            }
6903          }          }
6904            !!!nack ('t343.1');
6905          !!!next-token;          !!!next-token;
6906          redo B;          next B;
6907        } elsif ({        } elsif ({
6908                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6909                  div => 1, dl => 1, fieldset => 1,  
6910                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6911                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6912                    center => 1, datagrid => 1, details => 1, dialog => 1,
6913                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6914                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6915                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6916                    section => 1, ul => 1,
6917                    ## NOTE: As normal, but drops leading newline
6918                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6919                    ## NOTE: As normal, but interacts with the form element pointer
6920                  form => 1,                  form => 1,
6921                    
6922                  table => 1,                  table => 1,
6923                  hr => 1,                  hr => 1,
6924                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 5656  sub _tree_construction_main ($) { Line 6926  sub _tree_construction_main ($) {
6926            !!!cp ('t350');            !!!cp ('t350');
6927            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
6928            ## Ignore the token            ## Ignore the token
6929              !!!nack ('t350.1');
6930            !!!next-token;            !!!next-token;
6931            redo B;            next B;
6932          }          }
6933    
6934          ## has a p element in scope          ## has a p element in scope
6935          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6936            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6937              !!!cp ('t344');              !!!cp ('t344');
6938              !!!back-token;              !!!back-token; # <form>
6939              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6940                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
6941              redo B;              next B;
6942            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6943              !!!cp ('t345');              !!!cp ('t345');
6944              last INSCOPE;              last INSCOPE;
6945            }            }
# Line 5679  sub _tree_construction_main ($) { Line 6947  sub _tree_construction_main ($) {
6947                        
6948          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6949          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6950              !!!nack ('t346.1');
6951            !!!next-token;            !!!next-token;
6952            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6953              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5695  sub _tree_construction_main ($) { Line 6964  sub _tree_construction_main ($) {
6964            !!!cp ('t347.1');            !!!cp ('t347.1');
6965            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
6966    
6967              !!!nack ('t347.2');
6968            !!!next-token;            !!!next-token;
6969          } elsif ($token->{tag_name} eq 'table') {          } elsif ($token->{tag_name} eq 'table') {
6970            !!!cp ('t382');            !!!cp ('t382');
# Line 5702  sub _tree_construction_main ($) { Line 6972  sub _tree_construction_main ($) {
6972                        
6973            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
6974    
6975              !!!nack ('t382.1');
6976            !!!next-token;            !!!next-token;
6977          } elsif ($token->{tag_name} eq 'hr') {          } elsif ($token->{tag_name} eq 'hr') {
6978            !!!cp ('t386');            !!!cp ('t386');
6979            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6980                    
6981              !!!nack ('t386.1');
6982            !!!next-token;            !!!next-token;
6983          } else {          } else {
6984            !!!cp ('t347');            !!!nack ('t347.1');
6985            !!!next-token;            !!!next-token;
6986          }          }
6987          redo B;          next B;
6988        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
6989          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
6990    
6991            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6992              ## Interpreted as <li><foo/></li><li/> (non-conforming)
6993              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6994              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6995              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6996              ## object (Fx)
6997              ## Generate non-tree (non-conforming)
6998              ## basefont (IE7 (where basefont is non-void)), center (IE),
6999              ## form (IE), hn (IE)
7000            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7001              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7002              ## div (Fx, S)
7003    
7004            my $non_optional;
7005            my $i = -1;
7006    
7007            ## 1.
7008            for my $node (reverse @{$self->{open_elements}}) {
7009              if ($node->[1] & LI_EL) {
7010                ## 2. (a) As if </li>
7011                {
7012                  ## If no </li> - not applied
7013                  #
7014    
7015                  ## Otherwise
7016    
7017                  ## 1. generate implied end tags, except for </li>
7018                  #
7019    
7020                  ## 2. If current node != "li", parse error
7021                  if ($non_optional) {
7022                    !!!parse-error (type => 'not closed',
7023                                    text => $non_optional->[0]->manakai_local_name,
7024                                    token => $token);
7025                    !!!cp ('t355');
7026                  } else {
7027                    !!!cp ('t356');
7028                  }
7029    
7030                  ## 3. Pop
7031                  splice @{$self->{open_elements}}, $i;
7032                }
7033    
7034                last; ## 2. (b) goto 5.
7035              } elsif (
7036                       ## NOTE: not "formatting" and not "phrasing"
7037                       ($node->[1] & SPECIAL_EL or
7038                        $node->[1] & SCOPING_EL) and
7039                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7040    
7041                       (not $node->[1] & ADDRESS_EL) &
7042                       (not $node->[1] & DIV_EL) &
7043                       (not $node->[1] & P_EL)) {
7044                ## 3.
7045                !!!cp ('t357');
7046                last; ## goto 5.
7047              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7048                !!!cp ('t358');
7049                #
7050              } else {
7051                !!!cp ('t359');
7052                $non_optional ||= $node;
7053                #
7054              }
7055              ## 4.
7056              ## goto 2.
7057              $i--;
7058            }
7059    
7060            ## 5. (a) has a |p| element in scope
7061          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7062            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7063              !!!cp ('t353');              !!!cp ('t353');
7064              !!!back-token;  
7065                ## NOTE: |<p><li>|, for example.
7066    
7067                !!!back-token; # <x>
7068              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7069                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7070              redo B;              next B;
7071            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7072              !!!cp ('t354');              !!!cp ('t354');
7073              last INSCOPE;              last INSCOPE;
7074            }            }
7075          } # INSCOPE          } # INSCOPE
7076              
7077          ## Step 1          ## 5. (b) insert
7078            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7079            !!!nack ('t359.1');
7080            !!!next-token;
7081            next B;
7082          } elsif ($token->{tag_name} eq 'dt' or
7083                   $token->{tag_name} eq 'dd') {
7084            ## NOTE: As normal, but imply </dt> or </dd> when ...
7085    
7086            my $non_optional;
7087          my $i = -1;          my $i = -1;
7088          my $node = $self->{open_elements}->[$i];  
7089          my $li_or_dtdd = {li => {li => 1},          ## 1.
7090                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
7091                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7092          LI: {              ## 2. (a) As if </li>
7093            ## Step 2              {
7094            if ($li_or_dtdd->{$node->[1]}) {                ## If no </li> - not applied
7095              if ($i != -1) {                #
7096                !!!cp ('t355');  
7097                !!!parse-error (type => 'end tag missing:'.                ## Otherwise
7098                                $self->{open_elements}->[-1]->[1], token => $token);  
7099              } else {                ## 1. generate implied end tags, except for </dt> or </dd>
7100                !!!cp ('t356');                #
7101    
7102                  ## 2. If current node != "dt"|"dd", parse error
7103                  if ($non_optional) {
7104                    !!!parse-error (type => 'not closed',
7105                                    text => $non_optional->[0]->manakai_local_name,
7106                                    token => $token);
7107                    !!!cp ('t355.1');
7108                  } else {
7109                    !!!cp ('t356.1');
7110                  }
7111    
7112                  ## 3. Pop
7113                  splice @{$self->{open_elements}}, $i;
7114              }              }
7115              splice @{$self->{open_elements}}, $i;  
7116              last LI;              last; ## 2. (b) goto 5.
7117              } elsif (
7118                       ## NOTE: not "formatting" and not "phrasing"
7119                       ($node->[1] & SPECIAL_EL or
7120                        $node->[1] & SCOPING_EL) and
7121                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7122    
7123                       (not $node->[1] & ADDRESS_EL) &
7124                       (not $node->[1] & DIV_EL) &
7125                       (not $node->[1] & P_EL)) {
7126                ## 3.
7127                !!!cp ('t357.1');
7128                last; ## goto 5.
7129              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7130                !!!cp ('t358.1');
7131                #
7132            } else {            } else {
7133              !!!cp ('t357');              !!!cp ('t359.1');
7134            }              $non_optional ||= $node;
7135                          #
           ## 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;  
7136            }            }
7137                        ## 4.
7138            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7139            $i--;            $i--;
7140            $node = $self->{open_elements}->[$i];          }
7141            redo LI;  
7142          } # LI          ## 5. (a) has a |p| element in scope
7143                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7144              if ($_->[1] & P_EL) {
7145                !!!cp ('t353.1');
7146                !!!back-token; # <x>
7147                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7148                          line => $token->{line}, column => $token->{column}};
7149                next B;
7150              } elsif ($_->[1] & SCOPING_EL) {
7151                !!!cp ('t354.1');
7152                last INSCOPE;
7153              }
7154            } # INSCOPE
7155    
7156            ## 5. (b) insert
7157          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7158            !!!nack ('t359.2');
7159          !!!next-token;          !!!next-token;
7160          redo B;          next B;
7161        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7162            ## NOTE: As normal, but effectively ends parsing
7163    
7164          ## has a p element in scope          ## has a p element in scope
7165          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7166            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7167              !!!cp ('t367');              !!!cp ('t367');
7168              !!!back-token;              !!!back-token; # <plaintext>
7169              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7170                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7171              redo B;              next B;
7172            } elsif ({            } elsif ($_->[1] & SCOPING_EL) {
                     applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7173              !!!cp ('t368');              !!!cp ('t368');
7174              last INSCOPE;              last INSCOPE;
7175            }            }
# Line 5795  sub _tree_construction_main ($) { Line 7179  sub _tree_construction_main ($) {
7179                        
7180          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7181                        
7182            !!!nack ('t368.1');
7183          !!!next-token;          !!!next-token;
7184          redo B;          next B;
7185        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7186          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7187            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7188            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7189              !!!cp ('t371');              !!!cp ('t371');
7190              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
7191                            
7192              !!!back-token;              !!!back-token; # <a>
7193              $token = {type => END_TAG_TOKEN, tag_name => 'a',              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7194                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7195              $formatting_end_tag->($token);              $formatting_end_tag->($token);
# Line 5835  sub _tree_construction_main ($) { Line 7220  sub _tree_construction_main ($) {
7220          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7221          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7222    
7223            !!!nack ('t374.1');
7224          !!!next-token;          !!!next-token;
7225          redo B;          next B;
7226        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7227          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7228    
7229          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7230          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7231            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7232            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7233              !!!cp ('t376');              !!!cp ('t376');
7234              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
7235              !!!back-token;              !!!back-token; # <nobr>
7236              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7237                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7238              redo B;              next B;
7239            } 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]}) {  
7240              !!!cp ('t377');              !!!cp ('t377');
7241              last INSCOPE;              last INSCOPE;
7242            }            }
# Line 5862  sub _tree_construction_main ($) { Line 7245  sub _tree_construction_main ($) {
7245          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7246          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7247                    
7248            !!!nack ('t377.1');
7249          !!!next-token;          !!!next-token;
7250          redo B;          next B;
7251        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7252          ## has a button element in scope          ## has a button element in scope
7253          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7254            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7255            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7256              !!!cp ('t378');              !!!cp ('t378');
7257              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
7258              !!!back-token;              !!!back-token; # <button>
7259              $token = {type => END_TAG_TOKEN, tag_name => 'button',              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7260                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
7261              redo B;              next B;
7262            } 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]}) {  
7263              !!!cp ('t379');              !!!cp ('t379');
7264              last INSCOPE;              last INSCOPE;
7265            }            }
# Line 5892  sub _tree_construction_main ($) { Line 7273  sub _tree_construction_main ($) {
7273    
7274          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7275    
7276            !!!nack ('t379.1');
7277          !!!next-token;          !!!next-token;
7278          redo B;          next B;
7279        } elsif ({        } elsif ({
7280                  xmp => 1,                  xmp => 1,
7281                  iframe => 1,                  iframe => 1,
7282                  noembed => 1,                  noembed => 1,
7283                  noframes => 1,                  noframes => 1, ## NOTE: This is an "as if in head" code clone.
7284                  noscript => 0, ## TODO: 1 if scripting is enabled                  noscript => 0, ## TODO: 1 if scripting is enabled
7285                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7286          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
# Line 5909  sub _tree_construction_main ($) { Line 7291  sub _tree_construction_main ($) {
7291          }          }
7292          ## NOTE: There is an "as if in body" code clone.          ## NOTE: There is an "as if in body" code clone.
7293          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
7294          redo B;          next B;
7295        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7296          !!!parse-error (type => 'isindex', token => $token);          !!!parse-error (type => 'isindex', token => $token);
7297                    
7298          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7299            !!!cp ('t389');            !!!cp ('t389');
7300            ## Ignore the token            ## Ignore the token
7301              !!!nack ('t389'); ## NOTE: Not acknowledged.
7302            !!!next-token;            !!!next-token;
7303            redo B;            next B;
7304          } else {          } else {
7305              !!!ack ('t391.1');
7306    
7307            my $at = $token->{attributes};            my $at = $token->{attributes};
7308            my $form_attrs;            my $form_attrs;
7309            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5962  sub _tree_construction_main ($) { Line 7347  sub _tree_construction_main ($) {
7347                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
7348                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
7349                           line => $token->{line}, column => $token->{column}};                           line => $token->{line}, column => $token->{column}};
           $token = shift @tokens;  
7350            !!!back-token (@tokens);            !!!back-token (@tokens);
7351            redo B;            !!!next-token;
7352              next B;
7353          }          }
7354        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7355          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7356          my $el;          my $el;
7357          !!!create-element ($el, $token->{tag_name}, $token->{attributes}, $token);          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7358                    
7359          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7360          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5978  sub _tree_construction_main ($) { Line 7363  sub _tree_construction_main ($) {
7363          $insert->($el);          $insert->($el);
7364                    
7365          my $text = '';          my $text = '';
7366            !!!nack ('t392.1');
7367          !!!next-token;          !!!next-token;
7368          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7369            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 6008  sub _tree_construction_main ($) { Line 7394  sub _tree_construction_main ($) {
7394            ## Ignore the token            ## Ignore the token
7395          } else {          } else {
7396            !!!cp ('t398');            !!!cp ('t398');
7397            !!!parse-error (type => 'in RCDATA:#'.$token->{type}, token => $token);            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7398          }          }
7399          !!!next-token;          !!!next-token;
7400            next B;
7401          } elsif ($token->{tag_name} eq 'rt' or
7402                   $token->{tag_name} eq 'rp') {
7403            ## has a |ruby| element in scope
7404            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7405              my $node = $self->{open_elements}->[$_];
7406              if ($node->[1] & RUBY_EL) {
7407                !!!cp ('t398.1');
7408                ## generate implied end tags
7409                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7410                  !!!cp ('t398.2');
7411                  pop @{$self->{open_elements}};
7412                }
7413                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7414                  !!!cp ('t398.3');
7415                  !!!parse-error (type => 'not closed',
7416                                  text => $self->{open_elements}->[-1]->[0]
7417                                      ->manakai_local_name,
7418                                  token => $token);
7419                  pop @{$self->{open_elements}}
7420                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7421                }
7422                last INSCOPE;
7423              } elsif ($node->[1] & SCOPING_EL) {
7424                !!!cp ('t398.4');
7425                last INSCOPE;
7426              }
7427            } # INSCOPE
7428    
7429            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7430    
7431            !!!nack ('t398.5');
7432            !!!next-token;
7433          redo B;          redo B;
7434          } elsif ($token->{tag_name} eq 'math' or
7435                   $token->{tag_name} eq 'svg') {
7436            $reconstruct_active_formatting_elements->($insert_to_current);
7437    
7438            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7439    
7440            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7441    
7442            ## "adjust foreign attributes" - done in insert-element-f
7443            
7444            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7445            
7446            if ($self->{self_closing}) {
7447              pop @{$self->{open_elements}};
7448              !!!ack ('t398.1');
7449            } else {
7450              !!!cp ('t398.2');
7451              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7452              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7453              ## mode, "in body" (not "in foreign content") secondary insertion
7454              ## mode, maybe.
7455            }
7456    
7457            !!!next-token;
7458            next B;
7459        } elsif ({        } elsif ({
7460                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7461                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6019  sub _tree_construction_main ($) { Line 7463  sub _tree_construction_main ($) {
7463                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7464                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7465          !!!cp ('t401');          !!!cp ('t401');
7466          !!!parse-error (type => 'in body:'.$token->{tag_name}, token => $token);          !!!parse-error (type => 'in body',
7467                            text => $token->{tag_name}, token => $token);
7468          ## Ignore the token          ## Ignore the token
7469            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7470            !!!next-token;
7471            next B;
7472          } elsif ($token->{tag_name} eq 'param' or
7473                   $token->{tag_name} eq 'source') {
7474            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7475            pop @{$self->{open_elements}};
7476    
7477            !!!ack ('t398.5');
7478          !!!next-token;          !!!next-token;
7479          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7480        } else {        } else {
7481          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
7482            !!!cp ('t384');            !!!cp ('t384');
# Line 6044  sub _tree_construction_main ($) { Line 7496  sub _tree_construction_main ($) {
7496              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
7497            !!!cp ('t380');            !!!cp ('t380');
7498            push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
7499              !!!nack ('t380.1');
7500          } elsif ({          } elsif ({
7501                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
7502                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
7503                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
7504                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7505            !!!cp ('t375');            !!!cp ('t375');
7506            push @$active_formatting_elements, $self->{open_elements}->[-1];            push @$active_formatting_elements, $self->{open_elements}->[-1];
7507              !!!nack ('t375.1');
7508          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
7509            !!!cp ('t388');            !!!cp ('t388');
7510            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7511            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7512              !!!ack ('t388.2');
7513          } elsif ({          } elsif ({
7514                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
7515                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
7516                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
7517            !!!cp ('t388.1');            !!!cp ('t388.1');
7518            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
7519              !!!ack ('t388.3');
7520          } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select') {
7521            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
7522                    
# Line 6074  sub _tree_construction_main ($) { Line 7529  sub _tree_construction_main ($) {
7529              !!!cp ('t400.2');              !!!cp ('t400.2');
7530              $self->{insertion_mode} = IN_SELECT_IM;              $self->{insertion_mode} = IN_SELECT_IM;
7531            }            }
7532              !!!nack ('t400.3');
7533          } else {          } else {
7534            !!!cp ('t402');            !!!nack ('t402');
7535          }          }
7536                    
7537          !!!next-token;          !!!next-token;
7538          redo B;          next B;
7539        }        }
7540      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7541        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
# Line 6087  sub _tree_construction_main ($) { Line 7543  sub _tree_construction_main ($) {
7543          my $i;          my $i;
7544          INSCOPE: {          INSCOPE: {
7545            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
7546              if ($_->[1] eq 'body') {              if ($_->[1] & BODY_EL) {
7547                !!!cp ('t405');                !!!cp ('t405');
7548                $i = $_;                $i = $_;
7549                last INSCOPE;                last INSCOPE;
7550              } elsif ({              } elsif ($_->[1] & SCOPING_EL) {
                       applet => 1, table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
7551                !!!cp ('t405.1');                !!!cp ('t405.1');
7552                last;                last;
7553              }              }
7554            }            }
7555    
7556            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7557                            value => $token->{tag_name}, token => $token);  
7558              !!!parse-error (type => 'unmatched end tag',
7559                              text => $token->{tag_name}, token => $token);
7560            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
7561            !!!next-token;            !!!next-token;
7562            redo B;            next B;
7563          } # INSCOPE          } # INSCOPE
7564    
7565          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
7566            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]}) {  
7567              !!!cp ('t403');              !!!cp ('t403');
7568              !!!parse-error (type => 'not closed:'.$_->[1], token => $token);              !!!parse-error (type => 'not closed',
7569                                text => $_->[0]->manakai_local_name,
7570                                token => $token);
7571              last;              last;
7572            } else {            } else {
7573              !!!cp ('t404');              !!!cp ('t404');
# Line 6123  sub _tree_construction_main ($) { Line 7576  sub _tree_construction_main ($) {
7576    
7577          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
7578          !!!next-token;          !!!next-token;
7579          redo B;          next B;
7580        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7581          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
7582            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7583            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7584                $self->{open_elements}->[1]->[1] & BODY_EL) {
7585              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7586              !!!cp ('t406');              !!!cp ('t406');
7587              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7588                                text => $self->{open_elements}->[1]->[0]
7589                                    ->manakai_local_name,
7590                                token => $token);
7591            } else {            } else {
7592              !!!cp ('t407');              !!!cp ('t407');
7593            }            }
7594            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7595            ## reprocess            ## reprocess
7596            redo B;            next B;
7597          } else {          } else {
7598            !!!cp ('t408');            !!!cp ('t408');
7599            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7600                              text => $token->{tag_name}, token => $token);
7601            ## Ignore the token            ## Ignore the token
7602            !!!next-token;            !!!next-token;
7603            redo B;            next B;
7604          }          }
7605        } elsif ({        } elsif ({
7606                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7607                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7608                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7609                    address => 1, article => 1, aside => 1, blockquote => 1,
7610                    center => 1, datagrid => 1, details => 1, dialog => 1,
7611                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7612                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7613                    ol => 1, pre => 1, section => 1, ul => 1,
7614    
7615                    ## NOTE: As normal, but ... optional tags
7616                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7617    
7618                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7619                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7620            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7621            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7622            ## </dd>" code.
7623    
7624          ## has an element in scope          ## has an element in scope
7625          my $i;          my $i;
7626          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7627            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7628            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7629              !!!cp ('t410');              !!!cp ('t410');
7630              $i = $_;              $i = $_;
7631              last INSCOPE;              last INSCOPE;
7632            } 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]}) {  
7633              !!!cp ('t411');              !!!cp ('t411');
7634              last INSCOPE;              last INSCOPE;
7635            }            }
# Line 6169  sub _tree_construction_main ($) { Line 7637  sub _tree_construction_main ($) {
7637    
7638          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7639            !!!cp ('t413');            !!!cp ('t413');
7640            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7641                              text => $token->{tag_name}, token => $token);
7642              ## NOTE: Ignore the token.
7643          } else {          } else {
7644            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7645            while ({            while ({
7646                      ## END_TAG_OPTIONAL_EL
7647                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7648                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7649                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7650                      option => 1,
7651                      optgroup => 1,
7652                    p => 1,                    p => 1,
7653                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7654                      rp => 1,
7655                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7656              !!!cp ('t409');              !!!cp ('t409');
7657              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7658            }            }
7659    
7660            ## Step 2.            ## Step 2.
7661            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7662                      ne $token->{tag_name}) {
7663              !!!cp ('t412');              !!!cp ('t412');
7664              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7665                                text => $self->{open_elements}->[-1]->[0]
7666                                    ->manakai_local_name,
7667                                token => $token);
7668            } else {            } else {
7669              !!!cp ('t414');              !!!cp ('t414');
7670            }            }
# Line 6200  sub _tree_construction_main ($) { Line 7679  sub _tree_construction_main ($) {
7679                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7680          }          }
7681          !!!next-token;          !!!next-token;
7682          redo B;          next B;
7683        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7684            ## NOTE: As normal, but interacts with the form element pointer
7685    
7686          undef $self->{form_element};          undef $self->{form_element};
7687    
7688          ## has an element in scope          ## has an element in scope
7689          my $i;          my $i;
7690          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7691            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7692            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7693              !!!cp ('t418');              !!!cp ('t418');
7694              $i = $_;              $i = $_;
7695              last INSCOPE;              last INSCOPE;
7696            } 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]}) {  
7697              !!!cp ('t419');              !!!cp ('t419');
7698              last INSCOPE;              last INSCOPE;
7699            }            }
# Line 6223  sub _tree_construction_main ($) { Line 7701  sub _tree_construction_main ($) {
7701    
7702          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7703            !!!cp ('t421');            !!!cp ('t421');
7704            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7705                              text => $token->{tag_name}, token => $token);
7706              ## NOTE: Ignore the token.
7707          } else {          } else {
7708            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7709            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7710              !!!cp ('t417');              !!!cp ('t417');
7711              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7712            }            }
7713                        
7714            ## Step 2.            ## Step 2.
7715            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7716                      ne $token->{tag_name}) {
7717              !!!cp ('t417.1');              !!!cp ('t417.1');
7718              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7719                                text => $self->{open_elements}->[-1]->[0]
7720                                    ->manakai_local_name,
7721                                token => $token);
7722            } else {            } else {
7723              !!!cp ('t420');              !!!cp ('t420');
7724            }              }  
# Line 6246  sub _tree_construction_main ($) { Line 7728  sub _tree_construction_main ($) {
7728          }          }
7729    
7730          !!!next-token;          !!!next-token;
7731          redo B;          next B;
7732        } elsif ({        } elsif ({
7733                    ## NOTE: As normal, except acts as a closer for any ...
7734                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7735                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7736          ## has an element in scope          ## has an element in scope
7737          my $i;          my $i;
7738          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7739            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7740            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7741              !!!cp ('t423');              !!!cp ('t423');
7742              $i = $_;              $i = $_;
7743              last INSCOPE;              last INSCOPE;
7744            } 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]}) {  
7745              !!!cp ('t424');              !!!cp ('t424');
7746              last INSCOPE;              last INSCOPE;
7747            }            }
# Line 6271  sub _tree_construction_main ($) { Line 7749  sub _tree_construction_main ($) {
7749    
7750          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7751            !!!cp ('t425.1');            !!!cp ('t425.1');
7752            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7753                              text => $token->{tag_name}, token => $token);
7754              ## NOTE: Ignore the token.
7755          } else {          } else {
7756            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7757            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7758              !!!cp ('t422');              !!!cp ('t422');
7759              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7760            }            }
7761                        
7762            ## Step 2.            ## Step 2.
7763            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7764                      ne $token->{tag_name}) {
7765              !!!cp ('t425');              !!!cp ('t425');
7766              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);              !!!parse-error (type => 'unmatched end tag',
7767                                text => $token->{tag_name}, token => $token);
7768            } else {            } else {
7769              !!!cp ('t426');              !!!cp ('t426');
7770            }            }
# Line 6294  sub _tree_construction_main ($) { Line 7774  sub _tree_construction_main ($) {
7774          }          }
7775                    
7776          !!!next-token;          !!!next-token;
7777          redo B;          next B;
7778        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7779            ## NOTE: As normal, except </p> implies <p> and ...
7780    
7781          ## has an element in scope          ## has an element in scope
7782            my $non_optional;
7783          my $i;          my $i;
7784          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7785            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7786            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7787              !!!cp ('t410.1');              !!!cp ('t410.1');
7788              $i = $_;              $i = $_;
7789              last INSCOPE;              last INSCOPE;
7790            } 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]}) {  
7791              !!!cp ('t411.1');              !!!cp ('t411.1');
7792              last INSCOPE;              last INSCOPE;
7793              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7794                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7795                !!!cp ('t411.2');
7796                #
7797              } else {
7798                !!!cp ('t411.3');
7799                $non_optional ||= $node;
7800                #
7801            }            }
7802          } # INSCOPE          } # INSCOPE
7803    
7804          if (defined $i) {          if (defined $i) {
7805            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7806              #
7807    
7808              ## 2. If current node != "p", parse error
7809              if ($non_optional) {
7810              !!!cp ('t412.1');              !!!cp ('t412.1');
7811              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);              !!!parse-error (type => 'not closed',
7812                                text => $non_optional->[0]->manakai_local_name,
7813                                token => $token);
7814            } else {            } else {
7815              !!!cp ('t414.1');              !!!cp ('t414.1');
7816            }            }
7817    
7818              ## 3. Pop
7819            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7820          } else {          } else {
7821            !!!cp ('t413.1');            !!!cp ('t413.1');
7822            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);            !!!parse-error (type => 'unmatched end tag',
7823                              text => $token->{tag_name}, token => $token);
7824    
7825            !!!cp ('t415.1');            !!!cp ('t415.1');
7826            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7827            my $el;            my $el;
7828            !!!create-element ($el, 'p',, $token);            !!!create-element ($el, $HTML_NS, 'p',, $token);
7829            $insert->($el);            $insert->($el);
7830            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7831          }          }
7832    
7833          !!!next-token;          !!!next-token;
7834          redo B;          next B;
7835        } elsif ({        } elsif ({
7836                  a => 1,                  a => 1,
7837                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7838                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7839                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7840                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7841          !!!cp ('t427');          !!!cp ('t427');
7842          $formatting_end_tag->($token);          $formatting_end_tag->($token);
7843          redo B;          next B;
7844        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7845          !!!cp ('t428');          !!!cp ('t428');
7846          !!!parse-error (type => 'unmatched end tag:br', token => $token);          !!!parse-error (type => 'unmatched end tag',
7847                            text => 'br', token => $token);
7848    
7849          ## As if <br>          ## As if <br>
7850          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7851                    
7852          my $el;          my $el;
7853          !!!create-element ($el, 'br',, $token);          !!!create-element ($el, $HTML_NS, 'br',, $token);
7854          $insert->($el);          $insert->($el);
7855                    
7856          ## Ignore the token.          ## Ignore the token.
7857          !!!next-token;          !!!next-token;
7858          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  
           
7859        } else {        } else {
7860            if ($token->{tag_name} eq 'sarcasm') {
7861              sleep 0.001; # take a deep breath
7862            }
7863    
7864          ## Step 1          ## Step 1
7865          my $node_i = -1;          my $node_i = -1;
7866          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7867    
7868          ## Step 2          ## Step 2
7869          S2: {          S2: {
7870            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7871              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7872              if ($node_tag_name eq $token->{tag_name}) {
7873              ## Step 1              ## Step 1
7874              ## generate implied end tags              ## generate implied end tags
7875              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7876                !!!cp ('t430');                !!!cp ('t430');
7877                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7878                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7879                  ## which seems wrong.
7880                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7881                  $node_i++;
7882              }              }
7883                    
7884              ## Step 2              ## Step 2
7885              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7886                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7887                $current_tag_name =~ tr/A-Z/a-z/;
7888                if ($current_tag_name ne $token->{tag_name}) {
7889                !!!cp ('t431');                !!!cp ('t431');
7890                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7891                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1], token => $token);                !!!parse-error (type => 'not closed',
7892                                  text => $self->{open_elements}->[-1]->[0]
7893                                      ->manakai_local_name,
7894                                  token => $token);
7895              } else {              } else {
7896                !!!cp ('t432');                !!!cp ('t432');
7897              }              }
7898                            
7899              ## Step 3              ## Step 3
7900              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7901    
7902              !!!next-token;              !!!next-token;
7903              last S2;              last S2;
7904            } else {            } else {
7905              ## Step 3              ## Step 3
7906              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7907                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7908                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7909                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7910                !!!cp ('t433');                !!!cp ('t433');
7911                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name}, token => $token);                !!!parse-error (type => 'unmatched end tag',
7912                                  text => $token->{tag_name}, token => $token);
7913                ## Ignore the token                ## Ignore the token
7914                !!!next-token;                !!!next-token;
7915                last S2;                last S2;
             }  
7916    
7917                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7918                  ## 9.27, "a" is a child of <dd> (conforming).  In
7919                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7920                  ## "a" is a child of both <body> and <dd>.
7921                }
7922                
7923              !!!cp ('t434');              !!!cp ('t434');
7924            }            }
7925                        
# Line 6434  sub _tree_construction_main ($) { Line 7930  sub _tree_construction_main ($) {
7930            ## Step 5;            ## Step 5;
7931            redo S2;            redo S2;
7932          } # S2          } # S2
7933          redo B;          next B;
7934        }        }
7935      }      }
7936      redo B;      next B;
7937      } continue { # B
7938        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7939          ## NOTE: The code below is executed in cases where it does not have
7940          ## to be, but it it is harmless even in those cases.
7941          ## has an element in scope
7942          INSCOPE: {
7943            for (reverse 0..$#{$self->{open_elements}}) {
7944              my $node = $self->{open_elements}->[$_];
7945              if ($node->[1] & FOREIGN_EL) {
7946                last INSCOPE;
7947              } elsif ($node->[1] & SCOPING_EL) {
7948                last;
7949              }
7950            }
7951            
7952            ## NOTE: No foreign element in scope.
7953            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7954          } # INSCOPE
7955        }
7956    } # B    } # B
7957    
7958    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6445  sub _tree_construction_main ($) { Line 7960  sub _tree_construction_main ($) {
7960    ## TODO: script stuffs    ## TODO: script stuffs
7961  } # _tree_construct_main  } # _tree_construct_main
7962    
7963  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7964    my $class = shift;    my $class = shift;
7965    my $node = shift;    my $node = shift;
7966    my $s = \$_[0];    #my $s = \$_[0];
7967    my $onerror = $_[1];    my $onerror = $_[1];
7968      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7969    
7970    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7971    
# Line 6468  sub set_inner_html ($$$) { Line 7984  sub set_inner_html ($$$) {
7984      }      }
7985    
7986      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7987      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7988    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7989      ## TODO: If non-html element      ## TODO: If non-html element
7990    
7991      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7992    
7993    ## TODO: Support for $get_wrapper
7994    
7995      ## Step 1 # MUST      ## Step 1 # MUST
7996      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7997      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6483  sub set_inner_html ($$$) { Line 8001  sub set_inner_html ($$$) {
8001    
8002      ## Step 8 # MUST      ## Step 8 # MUST
8003      my $i = 0;      my $i = 0;
8004      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8005      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8006      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8007        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8008        $input = $get_wrapper->($input);
8009        $p->{set_nc} = sub {
8010        my $self = shift;        my $self = shift;
8011    
8012        pop @{$self->{prev_char}};        my $char = '';
8013        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8014            $char = $self->{next_nc};
8015            delete $self->{next_nc};
8016            $self->{nc} = ord $char;
8017          } else {
8018            $self->{char_buffer} = '';
8019            $self->{char_buffer_pos} = 0;
8020            
8021            my $count = $input->manakai_read_until
8022                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8023                 $self->{char_buffer_pos});
8024            if ($count) {
8025              $self->{line_prev} = $self->{line};
8026              $self->{column_prev} = $self->{column};
8027              $self->{column}++;
8028              $self->{nc}
8029                  = ord substr ($self->{char_buffer},
8030                                $self->{char_buffer_pos}++, 1);
8031              return;
8032            }
8033            
8034            if ($input->read ($char, 1)) {
8035              $self->{nc} = ord $char;
8036            } else {
8037              $self->{nc} = -1;
8038              return;
8039            }
8040          }
8041    
8042          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8043          $p->{column}++;
8044    
8045        $self->{next_char} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
8046        $self->{next_char} = ord substr $$s, $i++, 1;          $p->{line}++;
8047        $column++;          $p->{column} = 0;
   
       if ($self->{next_char} == 0x000A) { # LF  
         $line++;  
         $column = 0;  
8048          !!!cp ('i1');          !!!cp ('i1');
8049        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8050          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8051          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8052          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8053          $column = 0;            $self->{next_nc} = $next;
8054            }
8055            $self->{nc} = 0x000A; # LF # MUST
8056            $p->{line}++;
8057            $p->{column} = 0;
8058          !!!cp ('i2');          !!!cp ('i2');
8059        } 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  
8060          !!!cp ('i4');          !!!cp ('i4');
8061          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8062          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8063        }        }
8064      };      };
8065      $p->{prev_char} = [-1, -1, -1];  
8066      $p->{next_char} = -1;      $p->{read_until} = sub {
8067              #my ($scalar, $specials_range, $offset) = @_;
8068          return 0 if defined $p->{next_nc};
8069    
8070          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8071          my $offset = $_[2] || 0;
8072          
8073          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8074            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8075            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8076              substr ($_[0], $offset)
8077                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8078              my $count = $+[0] - $-[0];
8079              if ($count) {
8080                $p->{column} += $count;
8081                $p->{char_buffer_pos} += $count;
8082                $p->{line_prev} = $p->{line};
8083                $p->{column_prev} = $p->{column} - 1;
8084                $p->{nc} = -1;
8085              }
8086              return $count;
8087            } else {
8088              return 0;
8089            }
8090          } else {
8091            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8092            if ($count) {
8093              $p->{column} += $count;
8094              $p->{column_prev} += $count;
8095              $p->{nc} = -1;
8096            }
8097            return $count;
8098          }
8099        }; # $p->{read_until}
8100    
8101      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8102        my (%opt) = @_;        my (%opt) = @_;
8103        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8104          my $column = $opt{column};
8105          if (defined $opt{token} and defined $opt{token}->{line}) {
8106            $line = $opt{token}->{line};
8107            $column = $opt{token}->{column};
8108          }
8109          warn "Parse error ($opt{type}) at line $line column $column\n";
8110      };      };
8111      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8112        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8113      };      };
8114            
8115        my $char_onerror = sub {
8116          my (undef, $type, %opt) = @_;
8117          $ponerror->(layer => 'encode',
8118                      line => $p->{line}, column => $p->{column} + 1,
8119                      %opt, type => $type);
8120        }; # $char_onerror
8121        $input->onerror ($char_onerror);
8122    
8123      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8124      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8125    
# Line 6546  sub set_inner_html ($$$) { Line 8141  sub set_inner_html ($$$) {
8141          unless defined $p->{content_model};          unless defined $p->{content_model};
8142          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8143    
8144      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8145          ## TODO: Foreign element OK?
8146    
8147      ## Step 3      ## Step 3
8148      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6556  sub set_inner_html ($$$) { Line 8152  sub set_inner_html ($$$) {
8152      $doc->append_child ($root);      $doc->append_child ($root);
8153    
8154      ## Step 5 # MUST      ## Step 5 # MUST
8155      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8156    
8157      undef $p->{head_element};      undef $p->{head_element};
8158    
# Line 6602  sub set_inner_html ($$$) { Line 8198  sub set_inner_html ($$$) {
8198      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8199    
8200      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8201    
8202        delete $p->{parse_error}; # delete loop
8203    } else {    } else {
8204      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";
8205    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24