/[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.100 by wakaba, Sun Mar 9 04:08:41 2008 UTC revision 1.198 by wakaba, Sat Oct 4 08:58:02 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      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 $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,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $c1_entity_char = {  my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
400    
401    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
402      my $self = shift;      my (%opt) = @_;
403      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
404      ## TODO: if $charset is supported    };
405      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      ## Step 1        };
410      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
411        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444      }      }
445    
446      ## Step 2      ## Step 2
447      if (defined $self->{input_encoding} and      my $byte_buffer = '';
448          $self->{input_encoding} eq $charset) {      for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        return;        last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476      }      }
477    
478      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
479          ':'.$charset, level => 'w');      ## TODO: <meta charset>
480    
481      ## Step 3      ## Step 5
482      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
483    
484      ## Step 4      ## Step 6
485      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599    }; # $self->{change_encoding}    }; # $self->{change_encoding}
600    
601    my @args = @_; shift @args; # $s    my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
620      $s = \ (Encode::decode ($charset, $$bytes_s));      
621      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $return = $self->parse_char_string ($s, @args);  
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_string  } # parse_byte_stream
648    
649  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 655  sub parse_byte_string ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 175  sub parse_string ($$$;$) { Line 676  sub parse_string ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
681    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
682    my $line = 1;    $self->{column_prev} = -1;
683    my $column = 0;    $self->{column} = 0;
684    $self->{set_next_char} = sub {    $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      $column++;        } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_char} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_char} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_char} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_char} == 0x0000) { # NULL        }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_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) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_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 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 844  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
844  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
847  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
848  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
849  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
850  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 855  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
855  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
861  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 877  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
877  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
879  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
902  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 913  sub TABLE_IMS ()      { 0b1000000 }
913  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
914  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
915  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
# Line 325  sub IN_TABLE_IM () { TABLE_IMS } Line 940  sub IN_TABLE_IM () { TABLE_IMS }
940  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 333  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 350  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 367  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 428  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 444  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          !!!emit ({type => END_OF_FILE_TOKEN});          $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149        } else {        } 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        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1159        !!!next-input-character;                    };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!emit ($token);                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167          !!!cp (13);          !!!cp (13);
1168          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
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 => '<'});  
   
           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},
1217                   column => $self->{column_prev}};
1218            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_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},
1227                                        column => $self->{column_prev}};
1228            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_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},
1235                              column => $self->{column_prev});
1236            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_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},
1249                              column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            ## $self->{next_char} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!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    
1265            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
# Line 551  sub _get_next_token ($) { Line 1273  sub _get_next_token ($) {
1273          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1277          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_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;  
1278    
1279                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281                redo A;          if (defined $self->{last_stag_name}) {
1282              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            }            $self->{s_kwd} = '';
1284            push @next_char, $self->{next_char};            ## Reconsume.
1285                    redo A;
           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 => '</'});  
             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            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_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} = {type => END_TAG_TOKEN,          $self->{ct}
1303                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1304                   tag_name => chr ($self->{nc} + 0x0020),
1305                   line => $l, column => $c};
1306          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif (0x0061 <= $self->{next_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};
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 "</>"
1322                            column => $self->{column_prev} - 1);
1323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_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;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338          !!!cp (33);          !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_char} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343                                      column => $self->{column_prev} - 1,
1344                                     };
1345            ## NOTE: $self->{nc} is intentionally left as is.
1346            ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        if ($self->{next_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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1415            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1417            #  ## 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 670  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1446            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1448            #  ## 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 703  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1487            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1489              !!!cp (47);              !!!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} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1507                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1526              !!!cp (53);              !!!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 819  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} = {name => chr ($self->{next_char}),          $self->{ca}
1552                                value => ''};              = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!cp (57);            !!!cp (57);
1564            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1625            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1627              !!!cp (67);              !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629            } else {            } else {
# Line 924  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1671            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1673              !!!cp (74);              !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675            } else {            } else {
# Line 976  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} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1692                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1709            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1711              !!!cp (80);              !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713            } else {            } else {
# Line 1025  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} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1729                                value => ''};            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{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 1082  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1792            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1794              !!!cp (91);              !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796            } else {            } else {
# Line 1107  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1844            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1846              !!!cp (98);              !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848            } else {            } else {
# Line 1156  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1895            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1897              !!!cp (104);              !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899            } else {            } else {
# Line 1200  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1944                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{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 1247  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1968            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1970              !!!cp (113);              !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972            } else {            } else {
# Line 1272  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 1285  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->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2015                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
2016            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2018              !!!cp (120);              !!!cp (120);
2019              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2020            } else {            } else {
# Line 1344  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        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070        BC: {            ## TODO: Different type than slash in start tag
2071          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072            !!!cp (124);            if ($self->{ct}->{attributes}) {
2073            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2074            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2075              } else {
2076            !!!emit ($token);              !!!cp ('124.5');
2077              }
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 ($token);          !!!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";
           $token->{data} .= chr ($self->{next_char});  
           !!!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 @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122        push @next_char, $self->{next_char};        ## consumes characters one-by-one basis.
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            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2133            !!!next-input-character;          $self->{state} = DATA_STATE;
2134            redo A;          ## reconsume
2135          } else {  
2136            !!!cp (128);          !!!emit ($self->{ct}); # comment
2137          }          redo A;
2138        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2139                 $self->{next_char} == 0x0064) { # d          !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## 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;
                     !!!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->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2298        if ($self->{next_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          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_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 {
2437          !!!cp (154);          !!!cp (154);
2438          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2439          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_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 1643  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 ({type => DOCTYPE_TOKEN, quirks => 1});          !!!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 ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          !!!cp (160);          !!!cp (160);
2485          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2486              = {type => DOCTYPE_TOKEN,          delete $self->{ct}->{quirks};
                name => chr ($self->{next_char}),  
                #quirks => 0,  
               };  
 ## 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    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3035    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042         $additional => 1,          !!!cp (221.5);
3043        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      !!!cp (1001);          !!!next-input-character;
3045      ## Don't consume          redo A;
3046      ## No error        } else {
3047      return undef;          !!!cp (221.6);
3048    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3049      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3050      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3051          $self->{next_char} == 0x0058) { # X          redo A;
3052        my $code;        }
3053        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3055          !!!next-input-character;          $self->{state} = DATA_STATE;
3056          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3057              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3058            !!!cp (1002);            !!!cp (221.7);
3059            $code ||= 0;            !!!emit ($self->{ct}); # character
           $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');  
           !!!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;  
3060          } else {          } else {
3061            !!!cp (1007);            !!!cp (221.8);
3062            !!!parse-error (type => 'no refc');            ## 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          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3115            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3116            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3117            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3118          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3119            !!!cp (1009);  
3120            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3121            $code = 0xFFFD;          !!!cp (997);
3122          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3123            !!!cp (1010);          ## Reconsume.
3124            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125            $code = 0x000A;                    line => $self->{line_prev},
3126          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3127            !!!cp (1011);                   });
3128            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3129            $code = $c1_entity_char->{$code};        } else {
3130          }          !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3133                  has_reference => 1};          ## Reconsume.
3134        } # X          redo A;
3135      } elsif (0x0030 <= $self->{next_char} and        }
3136               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3138        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3139                  !!!cp (995);
3140        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3141                  $self->{next_char} <= 0x0039) { # 0..9          $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181          !!!cp (1012);          !!!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');          !!!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);          !!!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);          !!!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');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!cp (1018);  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $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      } else {          !!!cp (992);
3218        !!!cp (1019);          $self->{state} = $self->{prev_state};
3219        !!!parse-error (type => 'bare nero');          ## Reconsume.
3220        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3222        return undef;                   });
3223      }          redo A;
3224    } elsif ((0x0041 <= $self->{next_char} and        } else {
3225              $self->{next_char} <= 0x005A) or          !!!cp (991);
3226             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3227              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3228      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3229      !!!next-input-character;          ## Reconsume.
3230            redo A;
3231      my $value = $entity_name;        }
3232      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3233      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3237             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3238             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3239               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3240              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3241               $self->{next_char} <= 0x007A) or # z          redo A;
3242              (0x0030 <= $self->{next_char} and # 0        } else {
3243               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3244              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3245        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3246        if (defined $EntityChar->{$entity_name}) {  
3247          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3248            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3249            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3250            $match = 1;  
3251            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3252            last;            !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261          } else {          } 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            !!!next-input-character;            ## Reconsume.
3266              redo A;
3267          }          }
3268        } else {        }
3269          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271          $match *= 2;          # 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;          !!!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      if ($match > 0) {        my $l = $self->{line_prev};
3309        !!!cp (1023);        my $c = $self->{column_prev};
3310        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3311      } elsif ($match < 0) {          !!!cp (1008);
3312        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3313        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3314          !!!cp (1024);                          line => $l, column => $c);
3315          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $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;
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 (1025);          !!!cp (1026);
3399          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero');  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value};  
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');  } # _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 2400  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 2454  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');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          !!!cp ('t2');          !!!cp ('t2');
3512          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3513          !!!parse-error (type => 'not HTML5');        } 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 2481  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 2579  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 2602  sub _tree_construction_initial ($) { Line 3652  sub _tree_construction_initial ($) {
3652                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!cp ('t14');        !!!cp ('t14');
3655        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
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 2623  sub _tree_construction_initial ($) { Line 3674  sub _tree_construction_initial ($) {
3674          !!!cp ('t17');          !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 3703  sub _tree_construction_root_element ($)
3703    B: {    B: {
3704        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!cp ('t19');          !!!cp ('t19');
3706          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
# Line 2665  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 2686  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});            !!!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');
3747              $self->{application_cache_selection}              $self->{application_cache_selection}
3748                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3749              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753            } else {            } else {
3754              !!!cp ('t25');              !!!cp ('t25');
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 2716  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');      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 2746  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 2774  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 2792  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 2911  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});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3984    
3985      ## Step 2      ## Step 2
3986      $insert->($el);      $insert->($el);
# Line 2922  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 2948  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});          !!!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});          !!!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 2961  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});      !!!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 2987  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});        !!!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 3010  sub _tree_construction_main ($) { Line 4081  sub _tree_construction_main ($) {
4081      !!!next-token;      !!!next-token;
4082    }; # $script_start_tag    }; # $script_start_tag
4083    
4084      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4085      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4086      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4087    
4088    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4089      my $tag_name = shift;      my $end_tag_token = shift;
4090        my $tag_name = $end_tag_token->{tag_name};
4091    
4092        ## NOTE: The adoption agency algorithm (AAA).
4093    
4094      FET: {      FET: {
4095        ## Step 1        ## Step 1
4096        my $formatting_element;        my $formatting_element;
4097        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4098        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4099          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4100              !!!cp ('t52');
4101              last AFE;
4102            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4103                         eq $tag_name) {
4104            !!!cp ('t51');            !!!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);          !!!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 3047  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);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
4134              return;              return;
4135            }            }
4136          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   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);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4148          return;          return;
4149        }        }
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!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);
4156        }        }
4157                
4158        ## Step 2        ## Step 2
# Line 3077  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4161        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4162          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4163          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4164              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4165              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4166               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4167            !!!cp ('t59');            !!!cp ('t59');
4168            $furthest_block = $node;            $furthest_block = $node;
4169            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
# Line 3166  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } # S7          } # S7  
4250                
4251        ## Step 8        ## Step 8
4252        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4253            my $foster_parent_element;
4254            my $next_sibling;
4255            OE: for (reverse 0..$#{$self->{open_elements}}) {
4256              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4257                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4258                                 if (defined $parent and $parent->node_type == 1) {
4259                                   !!!cp ('t65.1');
4260                                   $foster_parent_element = $parent;
4261                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4262                                 } else {
4263                                   !!!cp ('t65.2');
4264                                   $foster_parent_element
4265                                     = $self->{open_elements}->[$_ - 1]->[0];
4266                                 }
4267                                 last OE;
4268                               }
4269                             } # OE
4270                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4271                               unless defined $foster_parent_element;
4272            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4273            $open_tables->[-1]->[1] = 1; # tainted
4274          } else {
4275            !!!cp ('t65.3');
4276            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4277          }
4278                
4279        ## Step 9        ## Step 9
4280        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3212  sub _tree_construction_main ($) { Line 4320  sub _tree_construction_main ($) {
4320      } # FET      } # FET
4321    }; # $formatting_end_tag    }; # $formatting_end_tag
4322    
   ## NOTE: $open_tables->[-1]->[0] is the "current table".  
   ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.  
   my $open_tables = [[$self->{open_elements}->[0]->[0]]];  
   
4323    $insert = my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4324      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4325    }; # $insert_to_current    }; # $insert_to_current
4326    
4327    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4328      my $child = shift;      my $child = shift;
4329      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          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 3254  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');        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363        ## Ignore the token        ## Ignore the token
4364        ## Stay in the phase        ## Stay in the phase
4365        !!!next-token;        !!!next-token;
4366        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         !!!cp ('t74');  
         #  
       } else {  
         ## Generate implied end tags  
         while ({  
                 dd => 1, dt => 1, li => 1, p => 1,  
                }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!cp ('t75');  
           pop @{$self->{open_elements}};  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!cp ('t76');  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
 ## ISSUE: This case is never reached.  
           !!!cp ('t77');  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } else {  
           !!!cp ('t78');  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
4367      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4368               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4369        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370          !!!cp ('t79');          !!!cp ('t79');
4371          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4373        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          !!!cp ('t80');          !!!cp ('t80');
4375          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377        } else {        } else {
4378          !!!cp ('t81');          !!!cp ('t81');
4379        }        }
4380    
4381        !!!cp ('t82');        !!!cp ('t82');
4382        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
4383        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4384        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4385          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3319  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 3334  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) {
4411            !!!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            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4522            ## Reprocess.
4523            next B;
4524          } else {
4525            die "$0: $token->{type}: Unknown token type";        
4526          }
4527        }
4528    
4529        if ($self->{insertion_mode} & HEAD_IMS) {
4530        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4531          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4532            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4533              !!!cp ('t88.2');              !!!cp ('t88.2');
4534              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4535                #
4536            } else {            } else {
4537              !!!cp ('t88.1');              !!!cp ('t88.1');
4538              ## Ignore the token.              ## Ignore the token.
4539              !!!next-token;              #
             redo B;  
4540            }            }
4541            unless (length $token->{data}) {            unless (length $token->{data}) {
4542              !!!cp ('t88');              !!!cp ('t88');
4543              !!!next-token;              !!!next-token;
4544              redo B;              next B;
4545            }            }
4546    ## TODO: set $token->{column} appropriately
4547          }          }
4548    
4549          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4550            !!!cp ('t89');            !!!cp ('t89');
4551            ## As if <head>            ## As if <head>
4552            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4553            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4554            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4555                  [$self->{head_element}, $el_category->{head}];
4556    
4557            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4558            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3369  sub _tree_construction_main ($) { Line 4562  sub _tree_construction_main ($) {
4562            !!!cp ('t90');            !!!cp ('t90');
4563            ## As if </noscript>            ## As if </noscript>
4564            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4565            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4566                        
4567            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4568            ## As if </head>            ## As if </head>
# Line 3385  sub _tree_construction_main ($) { Line 4578  sub _tree_construction_main ($) {
4578            !!!cp ('t92');            !!!cp ('t92');
4579          }          }
4580    
4581              ## "after head" insertion mode          ## "after head" insertion mode
4582              ## As if <body>          ## As if <body>
4583              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4584              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4585              ## reprocess          ## reprocess
4586              redo B;          next B;
4587            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4588              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4589                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4590                  !!!cp ('t93');              !!!cp ('t93');
4591                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4592                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4593                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4594                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4595                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4596                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4597                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4598                  !!!cp ('t94');              !!!next-token;
4599                  #              next B;
4600                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4601                  !!!cp ('t95');              !!!cp ('t93.2');
4602                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4603                  ## Ignore the token                              token => $token);
4604                  !!!next-token;              ## Ignore the token
4605                  redo B;              !!!nack ('t93.3');
4606                }              !!!next-token;
4607              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4608                !!!cp ('t96');            } else {
4609                ## As if <head>              !!!cp ('t95');
4610                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4611                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4612                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4613                !!!nack ('t95.1');
4614                !!!next-token;
4615                next B;
4616              }
4617            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4618              !!!cp ('t96');
4619              ## As if <head>
4620              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4621              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4622              push @{$self->{open_elements}},
4623                  [$self->{head_element}, $el_category->{head}];
4624    
4625                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4626                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4627              } else {          } else {
4628                !!!cp ('t97');            !!!cp ('t97');
4629              }          }
4630    
4631              if ($token->{tag_name} eq 'base') {              if ($token->{tag_name} eq 'base') {
4632                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4633                  !!!cp ('t98');                  !!!cp ('t98');
4634                  ## As if </noscript>                  ## As if </noscript>
4635                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4636                  !!!parse-error (type => 'in noscript:base');                  !!!parse-error (type => 'in noscript', text => 'base',
4637                                    token => $token);
4638                                
4639                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4640                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
# Line 3440  sub _tree_construction_main ($) { Line 4645  sub _tree_construction_main ($) {
4645                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4646                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4647                  !!!cp ('t100');                  !!!cp ('t100');
4648                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head',
4649                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4650                    push @{$self->{open_elements}},
4651                        [$self->{head_element}, $el_category->{head}];
4652                } else {                } else {
4653                  !!!cp ('t101');                  !!!cp ('t101');
4654                }                }
4655                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4656                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});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               } else {  
                 !!!cp ('t103');  
               }  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
4657                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4658                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4659                  !!!nack ('t101.1');
4660                !!!next-token;                !!!next-token;
4661                redo B;                next B;
4662            } elsif ($token->{tag_name} eq 'link') {
4663              ## NOTE: There is a "as if in head" code clone.
4664              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4665                !!!cp ('t102');
4666                !!!parse-error (type => 'after head',
4667                                text => $token->{tag_name}, token => $token);
4668                push @{$self->{open_elements}},
4669                    [$self->{head_element}, $el_category->{head}];
4670              } else {
4671                !!!cp ('t103');
4672              }
4673              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4674              pop @{$self->{open_elements}};
4675              pop @{$self->{open_elements}} # <head>
4676                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4677              !!!ack ('t103.1');
4678              !!!next-token;
4679              next B;
4680            } elsif ($token->{tag_name} eq 'command' or
4681                     $token->{tag_name} eq 'eventsource') {
4682              if ($self->{insertion_mode} == IN_HEAD_IM) {
4683                ## NOTE: If the insertion mode at the time of the emission
4684                ## of the token was "before head", $self->{insertion_mode}
4685                ## is already changed to |IN_HEAD_IM|.
4686    
4687                ## NOTE: There is a "as if in head" code clone.
4688                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4689                pop @{$self->{open_elements}};
4690                pop @{$self->{open_elements}} # <head>
4691                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4692                !!!ack ('t103.2');
4693                !!!next-token;
4694                next B;
4695              } else {
4696                ## NOTE: "in head noscript" or "after head" insertion mode
4697                ## - in these cases, these tags are treated as same as
4698                ## normal in-body tags.
4699                !!!cp ('t103.3');
4700                #
4701              }
4702              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'meta') {
4703                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4704                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4705                  !!!cp ('t104');                  !!!cp ('t104');
4706                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head',
4707                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4708                    push @{$self->{open_elements}},
4709                        [$self->{head_element}, $el_category->{head}];
4710                } else {                } else {
4711                  !!!cp ('t105');                  !!!cp ('t105');
4712                }                }
4713                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4714                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                my $meta_el = pop @{$self->{open_elements}};
4715    
4716                unless ($self->{confident}) {                unless ($self->{confident}) {
4717                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4718                    !!!cp ('t106');                    !!!cp ('t106');
4719                      ## NOTE: Whether the encoding is supported or not is handled
4720                      ## in the {change_encoding} callback.
4721                    $self->{change_encoding}                    $self->{change_encoding}
4722                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4723                             $token);
4724                                        
4725                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4726                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4727                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4728                                                 ->{has_reference});                                                 ->{has_reference});
4729                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4730                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4731                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4732                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4733                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4734                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4735                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4736                      !!!cp ('t107');                      !!!cp ('t107');
4737                        ## NOTE: Whether the encoding is supported or not is handled
4738                        ## in the {change_encoding} callback.
4739                      $self->{change_encoding}                      $self->{change_encoding}
4740                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4741                               $token);
4742                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4743                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4744                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3525  sub _tree_construction_main ($) { Line 4766  sub _tree_construction_main ($) {
4766    
4767                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4768                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4769                  !!!ack ('t110.1');
4770                !!!next-token;                !!!next-token;
4771                redo B;                next B;
4772              } elsif ($token->{tag_name} eq 'title') {              } elsif ($token->{tag_name} eq 'title') {
4773                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4774                  !!!cp ('t111');                  !!!cp ('t111');
4775                  ## As if </noscript>                  ## As if </noscript>
4776                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4777                  !!!parse-error (type => 'in noscript:title');                  !!!parse-error (type => 'in noscript', text => 'title',
4778                                    token => $token);
4779                                
4780                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4781                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4782                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4783                  !!!cp ('t112');                  !!!cp ('t112');
4784                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head',
4785                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4786                    push @{$self->{open_elements}},
4787                        [$self->{head_element}, $el_category->{head}];
4788                } else {                } else {
4789                  !!!cp ('t113');                  !!!cp ('t113');
4790                }                }
# Line 3550  sub _tree_construction_main ($) { Line 4795  sub _tree_construction_main ($) {
4795                $parse_rcdata->(RCDATA_CONTENT_MODEL);                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4796                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4797                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4798                redo B;                next B;
4799              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4800                         $token->{tag_name} eq 'noframes') {
4801                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4802                ## insertion mode IN_HEAD_IM)                ## insertion mode IN_HEAD_IM)
4803                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4804                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4805                  !!!cp ('t114');                  !!!cp ('t114');
4806                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head',
4807                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4808                    push @{$self->{open_elements}},
4809                        [$self->{head_element}, $el_category->{head}];
4810                } else {                } else {
4811                  !!!cp ('t115');                  !!!cp ('t115');
4812                }                }
4813                $parse_rcdata->(CDATA_CONTENT_MODEL);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4814                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4815                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4816                redo B;                next B;
4817              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4818                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4819                  !!!cp ('t116');                  !!!cp ('t116');
4820                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4821                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4822                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4823                    !!!nack ('t116.1');
4824                  !!!next-token;                  !!!next-token;
4825                  redo B;                  next B;
4826                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4827                  !!!cp ('t117');                  !!!cp ('t117');
4828                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript', text => 'noscript',
4829                                    token => $token);
4830                  ## Ignore the token                  ## Ignore the token
4831                    !!!nack ('t117.1');
4832                  !!!next-token;                  !!!next-token;
4833                  redo B;                  next B;
4834                } else {                } else {
4835                  !!!cp ('t118');                  !!!cp ('t118');
4836                  #                  #
# Line 3589  sub _tree_construction_main ($) { Line 4840  sub _tree_construction_main ($) {
4840                  !!!cp ('t119');                  !!!cp ('t119');
4841                  ## As if </noscript>                  ## As if </noscript>
4842                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4843                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript', text => 'script',
4844                                    token => $token);
4845                                
4846                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4847                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4848                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4849                  !!!cp ('t120');                  !!!cp ('t120');
4850                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'after head',
4851                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => $token->{tag_name}, token => $token);
4852                    push @{$self->{open_elements}},
4853                        [$self->{head_element}, $el_category->{head}];
4854                } else {                } else {
4855                  !!!cp ('t121');                  !!!cp ('t121');
4856                }                }
# Line 3605  sub _tree_construction_main ($) { Line 4859  sub _tree_construction_main ($) {
4859                $script_start_tag->();                $script_start_tag->();
4860                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4861                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4862                redo B;                next B;
4863              } elsif ($token->{tag_name} eq 'body' or              } elsif ($token->{tag_name} eq 'body' or
4864                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4865                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4866                  !!!cp ('t122');                  !!!cp ('t122');
4867                  ## As if </noscript>                  ## As if </noscript>
4868                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4869                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4870                                    text => $token->{tag_name}, token => $token);
4871                                    
4872                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4873                  ## As if </head>                  ## As if </head>
# Line 3629  sub _tree_construction_main ($) { Line 4884  sub _tree_construction_main ($) {
4884                }                }
4885    
4886                ## "after head" insertion mode                ## "after head" insertion mode
4887                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4888                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4889                  !!!cp ('t126');                  !!!cp ('t126');
4890                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3639  sub _tree_construction_main ($) { Line 4894  sub _tree_construction_main ($) {
4894                } else {                } else {
4895                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4896                }                }
4897                  !!!nack ('t127.1');
4898                !!!next-token;                !!!next-token;
4899                redo B;                next B;
4900              } else {              } else {
4901                !!!cp ('t128');                !!!cp ('t128');
4902                #                #
# Line 3650  sub _tree_construction_main ($) { Line 4906  sub _tree_construction_main ($) {
4906                !!!cp ('t129');                !!!cp ('t129');
4907                ## As if </noscript>                ## As if </noscript>
4908                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4909                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4910                                  text => $token->{tag_name}, token => $token);
4911                                
4912                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4913                ## As if </head>                ## As if </head>
# Line 3669  sub _tree_construction_main ($) { Line 4926  sub _tree_construction_main ($) {
4926    
4927              ## "after head" insertion mode              ## "after head" insertion mode
4928              ## As if <body>              ## As if <body>
4929              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4930              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4931              ## reprocess              ## reprocess
4932              redo B;              !!!ack-later;
4933                next B;
4934            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4935              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4936                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4937                  !!!cp ('t132');                  !!!cp ('t132');
4938                  ## As if <head>                  ## As if <head>
4939                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4940                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4941                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4942                        [$self->{head_element}, $el_category->{head}];
4943    
4944                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4945                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4946                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4947                  !!!next-token;                  !!!next-token;
4948                  redo B;                  next B;
4949                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4950                  !!!cp ('t133');                  !!!cp ('t133');
4951                  ## As if </noscript>                  ## As if </noscript>
4952                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4953                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/',
4954                                    text => 'head', token => $token);
4955                                    
4956                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4957                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4958                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4959                  !!!next-token;                  !!!next-token;
4960                  redo B;                  next B;
4961                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4962                  !!!cp ('t134');                  !!!cp ('t134');
4963                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4964                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4965                  !!!next-token;                  !!!next-token;
4966                  redo B;                  next B;
4967                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4968                    !!!cp ('t134.1');
4969                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4970                                    token => $token);
4971                    ## Ignore the token
4972                    !!!next-token;
4973                    next B;
4974                } else {                } else {
4975                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
4976                }                }
4977              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4978                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3714  sub _tree_construction_main ($) { Line 4980  sub _tree_construction_main ($) {
4980                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4981                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
4982                  !!!next-token;                  !!!next-token;
4983                  redo B;                  next B;
4984                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4985                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4986                  !!!cp ('t137');                  !!!cp ('t137');
4987                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag',
4988                                    text => 'noscript', token => $token);
4989                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
4990                  !!!next-token;                  !!!next-token;
4991                  redo B;                  next B;
4992                } else {                } else {
4993                  !!!cp ('t138');                  !!!cp ('t138');
4994                  #                  #
# Line 3728  sub _tree_construction_main ($) { Line 4996  sub _tree_construction_main ($) {
4996              } elsif ({              } elsif ({
4997                        body => 1, html => 1,                        body => 1, html => 1,
4998                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4999                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5000                  !!!cp ('t139');                    $self->{insertion_mode} == IN_HEAD_IM or
5001                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 !!!create-element ($self->{head_element}, 'head');  
                 $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) {  
5002                  !!!cp ('t140');                  !!!cp ('t140');
5003                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5004                                    text => $token->{tag_name}, token => $token);
5005                    ## Ignore the token
5006                    !!!next-token;
5007                    next B;
5008                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5009                    !!!cp ('t140.1');
5010                    !!!parse-error (type => 'unmatched end tag',
5011                                    text => $token->{tag_name}, token => $token);
5012                  ## Ignore the token                  ## Ignore the token
5013                  !!!next-token;                  !!!next-token;
5014                  redo B;                  next B;
5015                } else {                } else {
5016                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5017                }                }
5018                              } elsif ($token->{tag_name} eq 'p') {
5019                #                !!!cp ('t142');
5020              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5021                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5022                       }->{$token->{tag_name}}) {                ## Ignore the token
5023                  !!!next-token;
5024                  next B;
5025                } elsif ($token->{tag_name} eq 'br') {
5026                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5027                  !!!cp ('t142');                  !!!cp ('t142.2');
5028                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5029                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5030                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5031                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5032      
5033                    ## Reprocess in the "after head" insertion mode...
5034                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5035                    !!!cp ('t143.2');
5036                    ## As if </head>
5037                    pop @{$self->{open_elements}};
5038                    $self->{insertion_mode} = AFTER_HEAD_IM;
5039      
5040                    ## Reprocess in the "after head" insertion mode...
5041                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5042                    !!!cp ('t143.3');
5043                    ## ISSUE: Two parse errors for <head><noscript></br>
5044                    !!!parse-error (type => 'unmatched end tag',
5045                                    text => 'br', token => $token);
5046                    ## As if </noscript>
5047                    pop @{$self->{open_elements}};
5048                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5049    
5050                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5051                } else {                  ## As if </head>
5052                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5053                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5054    
5055                #                  ## Reprocess in the "after head" insertion mode...
5056              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5057                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5058                  #                  #
5059                } else {                } else {
5060                  !!!cp ('t145');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5061                }                }
5062    
5063                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5064                  !!!parse-error (type => 'unmatched end tag',
5065                                  text => 'br', token => $token);
5066                  ## Ignore the token
5067                  !!!next-token;
5068                  next B;
5069                } else {
5070                  !!!cp ('t145');
5071                  !!!parse-error (type => 'unmatched end tag',
5072                                  text => $token->{tag_name}, token => $token);
5073                  ## Ignore the token
5074                  !!!next-token;
5075                  next B;
5076              }              }
5077    
5078              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5079                !!!cp ('t146');                !!!cp ('t146');
5080                ## As if </noscript>                ## As if </noscript>
5081                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5082                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5083                                  text => $token->{tag_name}, token => $token);
5084                                
5085                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5086                ## As if </head>                ## As if </head>
# Line 3798  sub _tree_construction_main ($) { Line 5096  sub _tree_construction_main ($) {
5096              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5097  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5098                !!!cp ('t148');                !!!cp ('t148');
5099                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5100                                  text => $token->{tag_name}, token => $token);
5101                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5102                !!!next-token;                !!!next-token;
5103                redo B;                next B;
5104              } else {              } else {
5105                !!!cp ('t149');                !!!cp ('t149');
5106              }              }
5107    
5108              ## "after head" insertion mode              ## "after head" insertion mode
5109              ## As if <body>              ## As if <body>
5110              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5111              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5112              ## reprocess              ## reprocess
5113              redo B;              next B;
5114            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5115              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5116            }            !!!cp ('t149.1');
5117    
5118              ## NOTE: As if <head>
5119              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5120              $self->{open_elements}->[-1]->[0]->append_child
5121                  ($self->{head_element});
5122              #push @{$self->{open_elements}},
5123              #    [$self->{head_element}, $el_category->{head}];
5124              #$self->{insertion_mode} = IN_HEAD_IM;
5125              ## NOTE: Reprocess.
5126    
5127              ## NOTE: As if </head>
5128              #pop @{$self->{open_elements}};
5129              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5130              ## NOTE: Reprocess.
5131              
5132              #
5133            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5134              !!!cp ('t149.2');
5135    
5136            ## ISSUE: An issue in the spec.            ## NOTE: As if </head>
5137              pop @{$self->{open_elements}};
5138              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5139              ## NOTE: Reprocess.
5140    
5141              #
5142            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5143              !!!cp ('t149.3');
5144    
5145              !!!parse-error (type => 'in noscript:#eof', token => $token);
5146    
5147              ## As if </noscript>
5148              pop @{$self->{open_elements}};
5149              #$self->{insertion_mode} = IN_HEAD_IM;
5150              ## NOTE: Reprocess.
5151    
5152              ## NOTE: As if </head>
5153              pop @{$self->{open_elements}};
5154              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5155              ## NOTE: Reprocess.
5156    
5157              #
5158            } else {
5159              !!!cp ('t149.4');
5160              #
5161            }
5162    
5163            ## NOTE: As if <body>
5164            !!!insert-element ('body',, $token);
5165            $self->{insertion_mode} = IN_BODY_IM;
5166            ## NOTE: Reprocess.
5167            next B;
5168          } else {
5169            die "$0: $token->{type}: Unknown token type";
5170          }
5171      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5172            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5173              !!!cp ('t150');              !!!cp ('t150');
# Line 3826  sub _tree_construction_main ($) { Line 5177  sub _tree_construction_main ($) {
5177              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5178    
5179              !!!next-token;              !!!next-token;
5180              redo B;              next B;
5181            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5182              if ({              if ({
5183                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3834  sub _tree_construction_main ($) { Line 5185  sub _tree_construction_main ($) {
5185                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5186                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5187                  ## have an element in table scope                  ## have an element in table scope
5188                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5189                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5190                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5191                      !!!cp ('t151');                      !!!cp ('t151');
5192                      $tn = $node->[1];  
5193                      last INSCOPE;                      ## Close the cell
5194                    } elsif ({                      !!!back-token; # <x>
5195                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5196                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5197                                  line => $token->{line},
5198                                  column => $token->{column}};
5199                        next B;
5200                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5201                      !!!cp ('t152');                      !!!cp ('t152');
5202                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5203                        last;
5204                    }                    }
5205                  } # INSCOPE                  }
5206                    unless (defined $tn) {  
5207                      !!!cp ('t153');                  !!!cp ('t153');
5208  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
5209                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      text => $token->{tag_name}, token => $token);
5210                      ## Ignore the token                  ## Ignore the token
5211                      !!!next-token;                  !!!nack ('t153.1');
5212                      redo B;                  !!!next-token;
5213                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo B;  
5214                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5215                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5216                                    token => $token);
5217                                    
5218                  ## As if </caption>                  ## NOTE: As if </caption>.
5219                  ## have a table element in table scope                  ## have a table element in table scope
5220                  my $i;                  my $i;
5221                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5222                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5223                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5224                      !!!cp ('t155');                      if ($node->[1] & CAPTION_EL) {
5225                      $i = $_;                        !!!cp ('t155');
5226                      last INSCOPE;                        $i = $_;
5227                    } elsif ({                        last INSCOPE;
5228                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5229                             }->{$node->[1]}) {                        !!!cp ('t156');
5230                      !!!cp ('t156');                        last;
5231                      last INSCOPE;                      }
5232                    }                    }
5233    
5234                      !!!cp ('t157');
5235                      !!!parse-error (type => 'start tag not allowed',
5236                                      text => $token->{tag_name}, token => $token);
5237                      ## Ignore the token
5238                      !!!nack ('t157.1');
5239                      !!!next-token;
5240                      next B;
5241                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t157');  
 ## TODO: this type is wrong.  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5242                                    
5243                  ## generate implied end tags                  ## generate implied end tags
5244                  while ({                  while ($self->{open_elements}->[-1]->[1]
5245                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5246                    !!!cp ('t158');                    !!!cp ('t158');
5247                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5248                  }                  }
5249    
5250                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5251                    !!!cp ('t159');                    !!!cp ('t159');
5252                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5253                                      text => $self->{open_elements}->[-1]->[0]
5254                                          ->manakai_local_name,
5255                                      token => $token);
5256                  } else {                  } else {
5257                    !!!cp ('t160');                    !!!cp ('t160');
5258                  }                  }
# Line 3912  sub _tree_construction_main ($) { Line 5264  sub _tree_construction_main ($) {
5264                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5265                                    
5266                  ## reprocess                  ## reprocess
5267                  redo B;                  !!!ack-later;
5268                    next B;
5269                } else {                } else {
5270                  !!!cp ('t161');                  !!!cp ('t161');
5271                  #                  #
# Line 3928  sub _tree_construction_main ($) { Line 5281  sub _tree_construction_main ($) {
5281                  my $i;                  my $i;
5282                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5283                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5284                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5285                      !!!cp ('t163');                      !!!cp ('t163');
5286                      $i = $_;                      $i = $_;
5287                      last INSCOPE;                      last INSCOPE;
5288                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5289                      !!!cp ('t164');                      !!!cp ('t164');
5290                      last INSCOPE;                      last INSCOPE;
5291                    }                    }
5292                  } # INSCOPE                  } # INSCOPE
5293                    unless (defined $i) {                    unless (defined $i) {
5294                      !!!cp ('t165');                      !!!cp ('t165');
5295                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
5296                                        text => $token->{tag_name},
5297                                        token => $token);
5298                      ## Ignore the token                      ## Ignore the token
5299                      !!!next-token;                      !!!next-token;
5300                      redo B;                      next B;
5301                    }                    }
5302                                    
5303                  ## generate implied end tags                  ## generate implied end tags
5304                  while ({                  while ($self->{open_elements}->[-1]->[1]
5305                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5306                    !!!cp ('t166');                    !!!cp ('t166');
5307                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5308                  }                  }
5309    
5310                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5311                            ne $token->{tag_name}) {
5312                    !!!cp ('t167');                    !!!cp ('t167');
5313                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5314                                      text => $self->{open_elements}->[-1]->[0]
5315                                          ->manakai_local_name,
5316                                      token => $token);
5317                  } else {                  } else {
5318                    !!!cp ('t168');                    !!!cp ('t168');
5319                  }                  }
# Line 3969  sub _tree_construction_main ($) { Line 5325  sub _tree_construction_main ($) {
5325                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5326                                    
5327                  !!!next-token;                  !!!next-token;
5328                  redo B;                  next B;
5329                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5330                  !!!cp ('t169');                  !!!cp ('t169');
5331                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5332                                    text => $token->{tag_name}, token => $token);
5333                  ## Ignore the token                  ## Ignore the token
5334                  !!!next-token;                  !!!next-token;
5335                  redo B;                  next B;
5336                } else {                } else {
5337                  !!!cp ('t170');                  !!!cp ('t170');
5338                  #                  #
# Line 3984  sub _tree_construction_main ($) { Line 5341  sub _tree_construction_main ($) {
5341                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5342                  ## have a table element in table scope                  ## have a table element in table scope
5343                  my $i;                  my $i;
5344                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5345                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5346                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5347                      !!!cp ('t171');                      if ($node->[1] & CAPTION_EL) {
5348                      $i = $_;                        !!!cp ('t171');
5349                      last INSCOPE;                        $i = $_;
5350                    } elsif ({                        last INSCOPE;
5351                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5352                             }->{$node->[1]}) {                        !!!cp ('t172');
5353                      !!!cp ('t172');                        last;
5354                      last INSCOPE;                      }
5355                    }                    }
5356    
5357                      !!!cp ('t173');
5358                      !!!parse-error (type => 'unmatched end tag',
5359                                      text => $token->{tag_name}, token => $token);
5360                      ## Ignore the token
5361                      !!!next-token;
5362                      next B;
5363                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5364                                    
5365                  ## generate implied end tags                  ## generate implied end tags
5366                  while ({                  while ($self->{open_elements}->[-1]->[1]
5367                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5368                    !!!cp ('t174');                    !!!cp ('t174');
5369                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5370                  }                  }
5371                                    
5372                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5373                    !!!cp ('t175');                    !!!cp ('t175');
5374                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5375                                      text => $self->{open_elements}->[-1]->[0]
5376                                          ->manakai_local_name,
5377                                      token => $token);
5378                  } else {                  } else {
5379                    !!!cp ('t176');                    !!!cp ('t176');
5380                  }                  }
# Line 4027  sub _tree_construction_main ($) { Line 5386  sub _tree_construction_main ($) {
5386                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5387                                    
5388                  !!!next-token;                  !!!next-token;
5389                  redo B;                  next B;
5390                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5391                  !!!cp ('t177');                  !!!cp ('t177');
5392                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5393                                    text => $token->{tag_name}, token => $token);
5394                  ## Ignore the token                  ## Ignore the token
5395                  !!!next-token;                  !!!next-token;
5396                  redo B;                  next B;
5397                } else {                } else {
5398                  !!!cp ('t178');                  !!!cp ('t178');
5399                  #                  #
# Line 4046  sub _tree_construction_main ($) { Line 5406  sub _tree_construction_main ($) {
5406                ## have an element in table scope                ## have an element in table scope
5407                my $i;                my $i;
5408                my $tn;                my $tn;
5409                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5410                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5411                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5412                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5413                    $i = $_;                      !!!cp ('t179');
5414                    last INSCOPE;                      $i = $_;
5415                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5416                    !!!cp ('t180');                      ## Close the cell
5417                    $tn = $node->[1];                      !!!back-token; # </x>
5418                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5419                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5420                  } elsif ({                                column => $token->{column}};
5421                            table => 1, html => 1,                      next B;
5422                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5423                    !!!cp ('t181');                      !!!cp ('t180');
5424                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
5425                        ## NOTE: There is exactly one |td| or |th| element
5426                        ## in scope in the stack of open elements by definition.
5427                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5428                        ## ISSUE: Can this be reached?
5429                        !!!cp ('t181');
5430                        last;
5431                      }
5432                  }                  }
5433                } # INSCOPE  
               unless (defined $i) {  
5434                  !!!cp ('t182');                  !!!cp ('t182');
5435                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5436                        text => $token->{tag_name}, token => $token);
5437                  ## Ignore the token                  ## Ignore the token
5438                  !!!next-token;                  !!!next-token;
5439                  redo B;                  next B;
5440                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5441              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5442                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5443                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5444                                  token => $token);
5445    
5446                ## As if </caption>                ## As if </caption>
5447                ## have a table element in table scope                ## have a table element in table scope
5448                my $i;                my $i;
5449                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5450                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5451                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5452                    !!!cp ('t184');                    !!!cp ('t184');
5453                    $i = $_;                    $i = $_;
5454                    last INSCOPE;                    last INSCOPE;
5455                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5456                    !!!cp ('t185');                    !!!cp ('t185');
5457                    last INSCOPE;                    last INSCOPE;
5458                  }                  }
5459                } # INSCOPE                } # INSCOPE
5460                unless (defined $i) {                unless (defined $i) {
5461                  !!!cp ('t186');                  !!!cp ('t186');
5462                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag',
5463                                    text => 'caption', token => $token);
5464                  ## Ignore the token                  ## Ignore the token
5465                  !!!next-token;                  !!!next-token;
5466                  redo B;                  next B;
5467                }                }
5468                                
5469                ## generate implied end tags                ## generate implied end tags
5470                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5471                  !!!cp ('t187');                  !!!cp ('t187');
5472                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5473                }                }
5474    
5475                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5476                  !!!cp ('t188');                  !!!cp ('t188');
5477                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5478                                    text => $self->{open_elements}->[-1]->[0]
5479                                        ->manakai_local_name,
5480                                    token => $token);
5481                } else {                } else {
5482                  !!!cp ('t189');                  !!!cp ('t189');
5483                }                }
# Line 4128  sub _tree_construction_main ($) { Line 5489  sub _tree_construction_main ($) {
5489                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5490    
5491                ## reprocess                ## reprocess
5492                redo B;                next B;
5493              } elsif ({              } elsif ({
5494                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5495                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5496                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5497                  !!!cp ('t190');                  !!!cp ('t190');
5498                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5499                                    text => $token->{tag_name}, token => $token);
5500                  ## Ignore the token                  ## Ignore the token
5501                  !!!next-token;                  !!!next-token;
5502                  redo B;                  next B;
5503                } else {                } else {
5504                  !!!cp ('t191');                  !!!cp ('t191');
5505                  #                  #
# Line 4148  sub _tree_construction_main ($) { Line 5510  sub _tree_construction_main ($) {
5510                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5511                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5512                !!!cp ('t192');                !!!cp ('t192');
5513                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5514                                  text => $token->{tag_name}, token => $token);
5515                ## Ignore the token                ## Ignore the token
5516                !!!next-token;                !!!next-token;
5517                redo B;                next B;
5518              } else {              } else {
5519                !!!cp ('t193');                !!!cp ('t193');
5520                #                #
5521              }              }
5522          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5523            for my $entry (@{$self->{open_elements}}) {
5524              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5525                !!!cp ('t75');
5526                !!!parse-error (type => 'in body:#eof', token => $token);
5527                last;
5528              }
5529            }
5530    
5531            ## Stop parsing.
5532            last B;
5533        } else {        } else {
5534          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5535        }        }
# Line 4165  sub _tree_construction_main ($) { Line 5539  sub _tree_construction_main ($) {
5539      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5540        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5541          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5542              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5543            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5544                                
5545            unless (length $token->{data}) {            unless (length $token->{data}) {
5546              !!!cp ('t194');              !!!cp ('t194');
5547              !!!next-token;              !!!next-token;
5548              redo B;              next B;
5549            } else {            } else {
5550              !!!cp ('t195');              !!!cp ('t195');
5551            }            }
5552          }          }
5553    
5554              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5555    
5556              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5557              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 4185  sub _tree_construction_main ($) { Line 5559  sub _tree_construction_main ($) {
5559              ## result in a new Text node.              ## result in a new Text node.
5560              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5561                            
5562              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]}) {  
5563                # MUST                # MUST
5564                my $foster_parent_element;                my $foster_parent_element;
5565                my $next_sibling;                my $next_sibling;
5566                my $prev_sibling;                my $prev_sibling;
5567                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5568                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5569                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5570                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5571                      !!!cp ('t196');                      !!!cp ('t196');
# Line 4229  sub _tree_construction_main ($) { Line 5600  sub _tree_construction_main ($) {
5600          }          }
5601                            
5602          !!!next-token;          !!!next-token;
5603          redo B;          next B;
5604        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5605              if ({          if ({
5606                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5607                   th => 1, td => 1,               th => 1, td => 1,
5608                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5609                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5610                  ## Clear back to table context              ## Clear back to table context
5611                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5612                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5613                    !!!cp ('t201');                !!!cp ('t201');
5614                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5615                  }              }
5616                                
5617                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5618                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5619                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5620                }            }
5621              
5622                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5623                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5624                    !!!cp ('t202');                !!!cp ('t202');
5625                    !!!parse-error (type => 'missing start tag:tr');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5626                  }              }
5627                                    
5628                  ## Clear back to table body context              ## Clear back to table body context
5629                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5630                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5631                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5632                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5633                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5634                    pop @{$self->{open_elements}};              }
                 }  
5635                                    
5636                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5637                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5638                    !!!cp ('t204');                    !!!cp ('t204');
5639                    !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5640                      !!!nack ('t204');
5641                    !!!next-token;                    !!!next-token;
5642                    redo B;                    next B;
5643                  } else {                  } else {
5644                    !!!cp ('t205');                    !!!cp ('t205');
5645                    !!!insert-element ('tr');                    !!!insert-element ('tr',, $token);
5646                    ## reprocess in the "in row" insertion mode                    ## reprocess in the "in row" insertion mode
5647                  }                  }
5648                } else {                } else {
# Line 4279  sub _tree_construction_main ($) { Line 5650  sub _tree_construction_main ($) {
5650                }                }
5651    
5652                ## Clear back to table row context                ## Clear back to table row context
5653                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5654                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5655                  !!!cp ('t207');                  !!!cp ('t207');
5656                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5657                }                }
5658                                
5659                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5660                $self->{insertion_mode} = IN_CELL_IM;                $self->{insertion_mode} = IN_CELL_IM;
5661    
5662                push @$active_formatting_elements, ['#marker', ''];                push @$active_formatting_elements, ['#marker', ''];
5663                                
5664                  !!!nack ('t207.1');
5665                !!!next-token;                !!!next-token;
5666                redo B;                next B;
5667              } elsif ({              } elsif ({
5668                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5669                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
# Line 4304  sub _tree_construction_main ($) { Line 5675  sub _tree_construction_main ($) {
5675                  my $i;                  my $i;
5676                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5677                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5678                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5679                      !!!cp ('t208');                      !!!cp ('t208');
5680                      $i = $_;                      $i = $_;
5681                      last INSCOPE;                      last INSCOPE;
5682                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
5683                      !!!cp ('t209');                      !!!cp ('t209');
5684                      last INSCOPE;                      last INSCOPE;
5685                    }                    }
5686                  } # INSCOPE                  } # INSCOPE
5687                  unless (defined $i) {                  unless (defined $i) {
5688                   !!!cp ('t210');                    !!!cp ('t210');
5689  ## TODO: This type is wrong.  ## TODO: This type is wrong.
5690                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmacthed end tag',
5691                                      text => $token->{tag_name}, token => $token);
5692                    ## Ignore the token                    ## Ignore the token
5693                      !!!nack ('t210.1');
5694                    !!!next-token;                    !!!next-token;
5695                    redo B;                    next B;
5696                  }                  }
5697                                    
5698                  ## Clear back to table row context                  ## Clear back to table row context
5699                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5700                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5701                    !!!cp ('t211');                    !!!cp ('t211');
5702                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5703                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4341  sub _tree_construction_main ($) { Line 5708  sub _tree_construction_main ($) {
5708                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5709                    !!!cp ('t212');                    !!!cp ('t212');
5710                    ## reprocess                    ## reprocess
5711                    redo B;                    !!!ack-later;
5712                      next B;
5713                  } else {                  } else {
5714                    !!!cp ('t213');                    !!!cp ('t213');
5715                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4353  sub _tree_construction_main ($) { Line 5721  sub _tree_construction_main ($) {
5721                  my $i;                  my $i;
5722                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5723                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5724                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5725                      !!!cp ('t214');                      !!!cp ('t214');
5726                      $i = $_;                      $i = $_;
5727                      last INSCOPE;                      last INSCOPE;
5728                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5729                      !!!cp ('t215');                      !!!cp ('t215');
5730                      last INSCOPE;                      last INSCOPE;
5731                    }                    }
5732                  } # INSCOPE                  } # INSCOPE
5733                  unless (defined $i) {                  unless (defined $i) {
5734                    !!!cp ('t216');                    !!!cp ('t216');
5735  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5736                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
5737                                      text => $token->{tag_name}, token => $token);
5738                    ## Ignore the token                    ## Ignore the token
5739                      !!!nack ('t216.1');
5740                    !!!next-token;                    !!!next-token;
5741                    redo B;                    next B;
5742                  }                  }
5743    
5744                  ## Clear back to table body context                  ## Clear back to table body context
5745                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5746                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5747                    !!!cp ('t217');                    !!!cp ('t217');
5748                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5749                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4400  sub _tree_construction_main ($) { Line 5765  sub _tree_construction_main ($) {
5765    
5766                if ($token->{tag_name} eq 'col') {                if ($token->{tag_name} eq 'col') {
5767                  ## Clear back to table context                  ## Clear back to table context
5768                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5769                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5770                    !!!cp ('t219');                    !!!cp ('t219');
5771                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5772                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5773                  }                  }
5774                                    
5775                  !!!insert-element ('colgroup');                  !!!insert-element ('colgroup',, $token);
5776                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5777                  ## reprocess                  ## reprocess
5778                  redo B;                  !!!ack-later;
5779                    next B;
5780                } elsif ({                } elsif ({
5781                          caption => 1,                          caption => 1,
5782                          colgroup => 1,                          colgroup => 1,
5783                          tbody => 1, tfoot => 1, thead => 1,                          tbody => 1, tfoot => 1, thead => 1,
5784                         }->{$token->{tag_name}}) {                         }->{$token->{tag_name}}) {
5785                  ## Clear back to table context                  ## Clear back to table context
5786                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                  while (not ($self->{open_elements}->[-1]->[1]
5787                         $self->{open_elements}->[-1]->[1] ne 'html') {                                  & TABLE_SCOPING_EL)) {
5788                    !!!cp ('t220');                    !!!cp ('t220');
5789                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5790                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4427  sub _tree_construction_main ($) { Line 5793  sub _tree_construction_main ($) {
5793                  push @$active_formatting_elements, ['#marker', '']                  push @$active_formatting_elements, ['#marker', '']
5794                      if $token->{tag_name} eq 'caption';                      if $token->{tag_name} eq 'caption';
5795                                    
5796                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5797                  $self->{insertion_mode} = {                  $self->{insertion_mode} = {
5798                                             caption => IN_CAPTION_IM,                                             caption => IN_CAPTION_IM,
5799                                             colgroup => IN_COLUMN_GROUP_IM,                                             colgroup => IN_COLUMN_GROUP_IM,
# Line 4436  sub _tree_construction_main ($) { Line 5802  sub _tree_construction_main ($) {
5802                                             thead => IN_TABLE_BODY_IM,                                             thead => IN_TABLE_BODY_IM,
5803                                            }->{$token->{tag_name}};                                            }->{$token->{tag_name}};
5804                  !!!next-token;                  !!!next-token;
5805                  redo B;                  !!!nack ('t220.1');
5806                    next B;
5807                } else {                } else {
5808                  die "$0: in table: <>: $token->{tag_name}";                  die "$0: in table: <>: $token->{tag_name}";
5809                }                }
5810              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5811                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5812                                  text => $self->{open_elements}->[-1]->[0]
5813                                      ->manakai_local_name,
5814                                  token => $token);
5815    
5816                ## As if </table>                ## As if </table>
5817                ## have a table element in table scope                ## have a table element in table scope
5818                my $i;                my $i;
5819                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5820                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5821                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5822                    !!!cp ('t221');                    !!!cp ('t221');
5823                    $i = $_;                    $i = $_;
5824                    last INSCOPE;                    last INSCOPE;
5825                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5826                    !!!cp ('t222');                    !!!cp ('t222');
5827                    last INSCOPE;                    last INSCOPE;
5828                  }                  }
# Line 4463  sub _tree_construction_main ($) { Line 5830  sub _tree_construction_main ($) {
5830                unless (defined $i) {                unless (defined $i) {
5831                  !!!cp ('t223');                  !!!cp ('t223');
5832  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5833                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5834                                    token => $token);
5835                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5836                    !!!nack ('t223.1');
5837                  !!!next-token;                  !!!next-token;
5838                  redo B;                  next B;
5839                }                }
5840                                
5841    ## TODO: Followings are removed from the latest spec.
5842                ## generate implied end tags                ## generate implied end tags
5843                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5844                  !!!cp ('t224');                  !!!cp ('t224');
5845                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5846                }                }
5847    
5848                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5849                  !!!cp ('t225');                  !!!cp ('t225');
5850  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5851                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5852                                    text => $self->{open_elements}->[-1]->[0]
5853                                        ->manakai_local_name,
5854                                    token => $token);
5855                } else {                } else {
5856                  !!!cp ('t226');                  !!!cp ('t226');
5857                }                }
# Line 4490  sub _tree_construction_main ($) { Line 5861  sub _tree_construction_main ($) {
5861    
5862                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5863    
5864                ## reprocess            ## reprocess
5865                redo B;            !!!ack-later;
5866              next B;
5867          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5868            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5869              !!!cp ('t227.8');              !!!cp ('t227.8');
5870              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5871              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5872              redo B;              next B;
5873            } else {            } else {
5874              !!!cp ('t227.7');              !!!cp ('t227.7');
5875              #              #
# Line 4507  sub _tree_construction_main ($) { Line 5879  sub _tree_construction_main ($) {
5879              !!!cp ('t227.6');              !!!cp ('t227.6');
5880              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5881              $script_start_tag->();              $script_start_tag->();
5882              redo B;              next B;
5883            } else {            } else {
5884              !!!cp ('t227.5');              !!!cp ('t227.5');
5885              #              #
# Line 4518  sub _tree_construction_main ($) { Line 5890  sub _tree_construction_main ($) {
5890                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5891                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5892                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5893                  !!!parse-error (type => 'in table:'.$token->{tag_name});                  !!!parse-error (type => 'in table',
5894                                    text => $token->{tag_name}, token => $token);
5895    
5896                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5897    
5898                  ## TODO: form element pointer                  ## TODO: form element pointer
5899    
5900                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5901    
5902                  !!!next-token;                  !!!next-token;
5903                  redo B;                  !!!ack ('t227.2.1');
5904                    next B;
5905                } else {                } else {
5906                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5907                  #                  #
# Line 4545  sub _tree_construction_main ($) { Line 5919  sub _tree_construction_main ($) {
5919            #            #
5920          }          }
5921    
5922          !!!parse-error (type => 'in table:'.$token->{tag_name});          !!!parse-error (type => 'in table', text => $token->{tag_name},
5923                            token => $token);
5924    
5925          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5926          #          #
# Line 4556  sub _tree_construction_main ($) { Line 5931  sub _tree_construction_main ($) {
5931                my $i;                my $i;
5932                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5933                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5934                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5935                    !!!cp ('t228');                    !!!cp ('t228');
5936                    $i = $_;                    $i = $_;
5937                    last INSCOPE;                    last INSCOPE;
5938                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5939                    !!!cp ('t229');                    !!!cp ('t229');
5940                    last INSCOPE;                    last INSCOPE;
5941                  }                  }
5942                } # INSCOPE                } # INSCOPE
5943                unless (defined $i) {                unless (defined $i) {
5944                  !!!cp ('t230');                  !!!cp ('t230');
5945                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5946                                    text => $token->{tag_name}, token => $token);
5947                  ## Ignore the token                  ## Ignore the token
5948                    !!!nack ('t230.1');
5949                  !!!next-token;                  !!!next-token;
5950                  redo B;                  next B;
5951                } else {                } else {
5952                  !!!cp ('t232');                  !!!cp ('t232');
5953                }                }
5954    
5955                ## Clear back to table row context                ## Clear back to table row context
5956                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5957                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5958                  !!!cp ('t231');                  !!!cp ('t231');
5959  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
5960                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4589  sub _tree_construction_main ($) { Line 5963  sub _tree_construction_main ($) {
5963                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5964                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5965                !!!next-token;                !!!next-token;
5966                redo B;                !!!nack ('t231.1');
5967                  next B;
5968              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5969                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
5970                  ## As if </tr>                  ## As if </tr>
# Line 4597  sub _tree_construction_main ($) { Line 5972  sub _tree_construction_main ($) {
5972                  my $i;                  my $i;
5973                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5974                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5975                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
5976                      !!!cp ('t233');                      !!!cp ('t233');
5977                      $i = $_;                      $i = $_;
5978                      last INSCOPE;                      last INSCOPE;
5979                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5980                      !!!cp ('t234');                      !!!cp ('t234');
5981                      last INSCOPE;                      last INSCOPE;
5982                    }                    }
# Line 4611  sub _tree_construction_main ($) { Line 5984  sub _tree_construction_main ($) {
5984                  unless (defined $i) {                  unless (defined $i) {
5985                    !!!cp ('t235');                    !!!cp ('t235');
5986  ## TODO: The following is wrong.  ## TODO: The following is wrong.
5987                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag',
5988                                      text => $token->{type}, token => $token);
5989                    ## Ignore the token                    ## Ignore the token
5990                      !!!nack ('t236.1');
5991                    !!!next-token;                    !!!next-token;
5992                    redo B;                    next B;
5993                  }                  }
5994                                    
5995                  ## Clear back to table row context                  ## Clear back to table row context
5996                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5997                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5998                    !!!cp ('t236');                    !!!cp ('t236');
5999  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6000                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4636  sub _tree_construction_main ($) { Line 6010  sub _tree_construction_main ($) {
6010                  my $i;                  my $i;
6011                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6012                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6013                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6014                      !!!cp ('t237');                      !!!cp ('t237');
6015                      $i = $_;                      $i = $_;
6016                      last INSCOPE;                      last INSCOPE;
6017                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6018                      !!!cp ('t238');                      !!!cp ('t238');
6019                      last INSCOPE;                      last INSCOPE;
6020                    }                    }
6021                  } # INSCOPE                  } # INSCOPE
6022                  unless (defined $i) {                  unless (defined $i) {
6023                    !!!cp ('t239');                    !!!cp ('t239');
6024                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
6025                                      text => $token->{tag_name}, token => $token);
6026                    ## Ignore the token                    ## Ignore the token
6027                      !!!nack ('t239.1');
6028                    !!!next-token;                    !!!next-token;
6029                    redo B;                    next B;
6030                  }                  }
6031                                    
6032                  ## Clear back to table body context                  ## Clear back to table body context
6033                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6034                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6035                    !!!cp ('t240');                    !!!cp ('t240');
6036                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6037                  }                  }
# Line 4686  sub _tree_construction_main ($) { Line 6057  sub _tree_construction_main ($) {
6057                my $i;                my $i;
6058                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6059                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6060                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6061                    !!!cp ('t241');                    !!!cp ('t241');
6062                    $i = $_;                    $i = $_;
6063                    last INSCOPE;                    last INSCOPE;
6064                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6065                    !!!cp ('t242');                    !!!cp ('t242');
6066                    last INSCOPE;                    last INSCOPE;
6067                  }                  }
6068                } # INSCOPE                } # INSCOPE
6069                unless (defined $i) {                unless (defined $i) {
6070                  !!!cp ('t243');                  !!!cp ('t243');
6071                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6072                                    text => $token->{tag_name}, token => $token);
6073                  ## Ignore the token                  ## Ignore the token
6074                    !!!nack ('t243.1');
6075                  !!!next-token;                  !!!next-token;
6076                  redo B;                  next B;
6077                }                }
6078                                    
6079                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4711  sub _tree_construction_main ($) { Line 6082  sub _tree_construction_main ($) {
6082                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6083                                
6084                !!!next-token;                !!!next-token;
6085                redo B;                next B;
6086              } elsif ({              } elsif ({
6087                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6088                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4721  sub _tree_construction_main ($) { Line 6092  sub _tree_construction_main ($) {
6092                  my $i;                  my $i;
6093                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6094                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6095                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6096                      !!!cp ('t247');                      !!!cp ('t247');
6097                      $i = $_;                      $i = $_;
6098                      last INSCOPE;                      last INSCOPE;
6099                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6100                      !!!cp ('t248');                      !!!cp ('t248');
6101                      last INSCOPE;                      last INSCOPE;
6102                    }                    }
6103                  } # INSCOPE                  } # INSCOPE
6104                    unless (defined $i) {                    unless (defined $i) {
6105                      !!!cp ('t249');                      !!!cp ('t249');
6106                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
6107                                        text => $token->{tag_name}, token => $token);
6108                      ## Ignore the token                      ## Ignore the token
6109                        !!!nack ('t249.1');
6110                      !!!next-token;                      !!!next-token;
6111                      redo B;                      next B;
6112                    }                    }
6113                                    
6114                  ## As if </tr>                  ## As if </tr>
# Line 4745  sub _tree_construction_main ($) { Line 6116  sub _tree_construction_main ($) {
6116                  my $i;                  my $i;
6117                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6118                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6119                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6120                      !!!cp ('t250');                      !!!cp ('t250');
6121                      $i = $_;                      $i = $_;
6122                      last INSCOPE;                      last INSCOPE;
6123                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6124                      !!!cp ('t251');                      !!!cp ('t251');
6125                      last INSCOPE;                      last INSCOPE;
6126                    }                    }
6127                  } # INSCOPE                  } # INSCOPE
6128                    unless (defined $i) {                    unless (defined $i) {
6129                      !!!cp ('t252');                      !!!cp ('t252');
6130                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag',
6131                                        text => 'tr', token => $token);
6132                      ## Ignore the token                      ## Ignore the token
6133                        !!!nack ('t252.1');
6134                      !!!next-token;                      !!!next-token;
6135                      redo B;                      next B;
6136                    }                    }
6137                                    
6138                  ## Clear back to table row context                  ## Clear back to table row context
6139                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6140                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6141                    !!!cp ('t253');                    !!!cp ('t253');
6142  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6143                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4782  sub _tree_construction_main ($) { Line 6152  sub _tree_construction_main ($) {
6152                my $i;                my $i;
6153                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6154                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6155                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6156                    !!!cp ('t254');                    !!!cp ('t254');
6157                    $i = $_;                    $i = $_;
6158                    last INSCOPE;                    last INSCOPE;
6159                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6160                    !!!cp ('t255');                    !!!cp ('t255');
6161                    last INSCOPE;                    last INSCOPE;
6162                  }                  }
6163                } # INSCOPE                } # INSCOPE
6164                unless (defined $i) {                unless (defined $i) {
6165                  !!!cp ('t256');                  !!!cp ('t256');
6166                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6167                                    text => $token->{tag_name}, token => $token);
6168                  ## Ignore the token                  ## Ignore the token
6169                    !!!nack ('t256.1');
6170                  !!!next-token;                  !!!next-token;
6171                  redo B;                  next B;
6172                }                }
6173    
6174                ## Clear back to table body context                ## Clear back to table body context
6175                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6176                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6177                  !!!cp ('t257');                  !!!cp ('t257');
6178  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6179                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4812  sub _tree_construction_main ($) { Line 6181  sub _tree_construction_main ($) {
6181    
6182                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6183                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6184                  !!!nack ('t257.1');
6185                !!!next-token;                !!!next-token;
6186                redo B;                next B;
6187              } elsif ({              } elsif ({
6188                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6189                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6190                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6191                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6192                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6193                !!!cp ('t258');            !!!cp ('t258');
6194                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6195                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6196                !!!next-token;            ## Ignore the token
6197                redo B;            !!!nack ('t258.1');
6198               !!!next-token;
6199              next B;
6200          } else {          } else {
6201            !!!cp ('t259');            !!!cp ('t259');
6202            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6203                              text => $token->{tag_name}, token => $token);
6204    
6205            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6206            #            #
6207          }          }
6208          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6209            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6210                    @{$self->{open_elements}} == 1) { # redundant, maybe
6211              !!!parse-error (type => 'in body:#eof', token => $token);
6212              !!!cp ('t259.1');
6213              #
6214            } else {
6215              !!!cp ('t259.2');
6216              #
6217            }
6218    
6219            ## Stop parsing
6220            last B;
6221        } else {        } else {
6222          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6223        }        }
6224      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6225            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6226              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6227                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6228                unless (length $token->{data}) {                unless (length $token->{data}) {
6229                  !!!cp ('t260');                  !!!cp ('t260');
6230                  !!!next-token;                  !!!next-token;
6231                  redo B;                  next B;
6232                }                }
6233              }              }
6234                            
# Line 4851  sub _tree_construction_main ($) { Line 6237  sub _tree_construction_main ($) {
6237            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6238              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6239                !!!cp ('t262');                !!!cp ('t262');
6240                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6241                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6242                  !!!ack ('t262.1');
6243                !!!next-token;                !!!next-token;
6244                redo B;                next B;
6245              } else {              } else {
6246                !!!cp ('t263');                !!!cp ('t263');
6247                #                #
6248              }              }
6249            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6250              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6251                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6252                  !!!cp ('t264');                  !!!cp ('t264');
6253                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag',
6254                                    text => 'colgroup', token => $token);
6255                  ## Ignore the token                  ## Ignore the token
6256                  !!!next-token;                  !!!next-token;
6257                  redo B;                  next B;
6258                } else {                } else {
6259                  !!!cp ('t265');                  !!!cp ('t265');
6260                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6261                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6262                  !!!next-token;                  !!!next-token;
6263                  redo B;                              next B;            
6264                }                }
6265              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6266                !!!cp ('t266');                !!!cp ('t266');
6267                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag',
6268                                  text => 'col', token => $token);
6269                ## Ignore the token                ## Ignore the token
6270                !!!next-token;                !!!next-token;
6271                redo B;                next B;
6272              } else {              } else {
6273                !!!cp ('t267');                !!!cp ('t267');
6274                #                #
6275              }              }
6276            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6277              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6278            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6279              !!!cp ('t270.2');
6280              ## Stop parsing.
6281              last B;
6282            } else {
6283              ## NOTE: As if </colgroup>.
6284              !!!cp ('t270.1');
6285              pop @{$self->{open_elements}}; # colgroup
6286              $self->{insertion_mode} = IN_TABLE_IM;
6287              ## Reprocess.
6288              next B;
6289            }
6290          } else {
6291            die "$0: $token->{type}: Unknown token type";
6292          }
6293    
6294            ## As if </colgroup>            ## As if </colgroup>
6295            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6296              !!!cp ('t269');              !!!cp ('t269');
6297              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
6298                !!!parse-error (type => 'unmatched end tag',
6299                                text => 'colgroup', token => $token);
6300              ## Ignore the token              ## Ignore the token
6301                !!!nack ('t269.1');
6302              !!!next-token;              !!!next-token;
6303              redo B;              next B;
6304            } else {            } else {
6305              !!!cp ('t270');              !!!cp ('t270');
6306              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6307              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6308                !!!ack-later;
6309              ## reprocess              ## reprocess
6310              redo B;              next B;
6311            }            }
6312      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6313        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6314          !!!cp ('t271');          !!!cp ('t271');
6315          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6316          !!!next-token;          !!!next-token;
6317          redo B;          next B;
6318        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6319              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6320                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6321                  !!!cp ('t272');              !!!cp ('t272');
6322                  ## As if </option>              ## As if </option>
6323                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6324                } else {            } else {
6325                  !!!cp ('t273');              !!!cp ('t273');
6326                }            }
6327    
6328                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6329                !!!next-token;            !!!nack ('t273.1');
6330                redo B;            !!!next-token;
6331              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6332                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6333                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6334                  ## As if </option>              !!!cp ('t274');
6335                  pop @{$self->{open_elements}};              ## As if </option>
6336                } else {              pop @{$self->{open_elements}};
6337                  !!!cp ('t275');            } else {
6338                }              !!!cp ('t275');
6339              }
6340    
6341                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6342                  !!!cp ('t276');              !!!cp ('t276');
6343                  ## As if </optgroup>              ## As if </optgroup>
6344                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6345                } else {            } else {
6346                  !!!cp ('t277');              !!!cp ('t277');
6347                }            }
6348    
6349                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6350                !!!next-token;            !!!nack ('t277.1');
6351                redo B;            !!!next-token;
6352              } elsif ($token->{tag_name} eq 'select') {            next B;
6353  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ({
6354                !!!parse-error (type => 'not closed:select');                     select => 1, input => 1, textarea => 1,
6355                ## As if </select> instead                   }->{$token->{tag_name}} or
6356                ## have an element in table scope                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6357                my $i;                    {
6358                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     caption => 1, table => 1,
6359                  my $node = $self->{open_elements}->[$_];                     tbody => 1, tfoot => 1, thead => 1,
6360                  if ($node->[1] eq $token->{tag_name}) {                     tr => 1, td => 1, th => 1,
6361                    !!!cp ('t278');                    }->{$token->{tag_name}})) {
6362                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
6363                    last INSCOPE;            !!!parse-error (type => 'not closed', text => 'select',
6364                  } elsif ({                            token => $token);
6365                            table => 1, html => 1,            ## NOTE: As if the token were </select> (<select> case) or
6366                           }->{$node->[1]}) {            ## as if there were </select> (otherwise).
6367                    !!!cp ('t279');            ## have an element in table scope
6368                    last INSCOPE;            my $i;
6369                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6370                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6371                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6372                  !!!cp ('t280');                !!!cp ('t278');
6373                  !!!parse-error (type => 'unmatched end tag:select');                $i = $_;
6374                  ## Ignore the token                last INSCOPE;
6375                  !!!next-token;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6376                  redo B;                !!!cp ('t279');
6377                }                last INSCOPE;
6378                }
6379              } # INSCOPE
6380              unless (defined $i) {
6381                !!!cp ('t280');
6382                !!!parse-error (type => 'unmatched end tag',
6383                                text => 'select', token => $token);
6384                ## Ignore the token
6385                !!!nack ('t280.1');
6386                !!!next-token;
6387                next B;
6388              }
6389                                
6390                !!!cp ('t281');            !!!cp ('t281');
6391                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6392    
6393                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6394    
6395                !!!next-token;            if ($token->{tag_name} eq 'select') {
6396                redo B;              !!!nack ('t281.2');
6397                !!!next-token;
6398                next B;
6399              } else {
6400                !!!cp ('t281.1');
6401                !!!ack-later;
6402                ## Reprocess the token.
6403                next B;
6404              }
6405          } else {          } else {
6406            !!!cp ('t282');            !!!cp ('t282');
6407            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6408                              text => $token->{tag_name}, token => $token);
6409            ## Ignore the token            ## Ignore the token
6410              !!!nack ('t282.1');
6411            !!!next-token;            !!!next-token;
6412            redo B;            next B;
6413          }          }
6414        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6415              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6416                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6417                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6418                  !!!cp ('t283');              !!!cp ('t283');
6419                  ## As if </option>              ## As if </option>
6420                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6421                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6422                  !!!cp ('t284');              !!!cp ('t284');
6423                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6424                } else {            } else {
6425                  !!!cp ('t285');              !!!cp ('t285');
6426                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6427                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6428                }              ## Ignore the token
6429                !!!next-token;            }
6430                redo B;            !!!nack ('t285.1');
6431              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6432                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6433                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6434                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6435                } else {              !!!cp ('t286');
6436                  !!!cp ('t287');              pop @{$self->{open_elements}};
6437                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6438                  ## Ignore the token              !!!cp ('t287');
6439                }              !!!parse-error (type => 'unmatched end tag',
6440                !!!next-token;                              text => $token->{tag_name}, token => $token);
6441                redo B;              ## Ignore the token
6442              } elsif ($token->{tag_name} eq 'select') {            }
6443                ## have an element in table scope            !!!nack ('t287.1');
6444                my $i;            !!!next-token;
6445                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6446                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6447                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6448                    !!!cp ('t288');            my $i;
6449                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6450                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6451                  } elsif ({              if ($node->[1] & SELECT_EL) {
6452                            table => 1, html => 1,                !!!cp ('t288');
6453                           }->{$node->[1]}) {                $i = $_;
6454                    !!!cp ('t289');                last INSCOPE;
6455                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6456                  }                !!!cp ('t289');
6457                } # INSCOPE                last INSCOPE;
6458                unless (defined $i) {              }
6459                  !!!cp ('t290');            } # INSCOPE
6460                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6461                  ## Ignore the token              !!!cp ('t290');
6462                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6463                  redo B;                              text => $token->{tag_name}, token => $token);
6464                }              ## Ignore the token
6465                !!!nack ('t290.1');
6466                !!!next-token;
6467                next B;
6468              }
6469                                
6470                !!!cp ('t291');            !!!cp ('t291');
6471                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6472    
6473                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6474    
6475                !!!next-token;            !!!nack ('t291.1');
6476                redo B;            !!!next-token;
6477              } elsif ({            next B;
6478                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6479                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6480                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6481                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6482                     }->{$token->{tag_name}}) {
6483  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6484                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6485                              text => $token->{tag_name}, token => $token);
6486                                
6487                ## have an element in table scope            ## have an element in table scope
6488                my $i;            my $i;
6489                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6490                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6491                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6492                    !!!cp ('t292');                !!!cp ('t292');
6493                    $i = $_;                $i = $_;
6494                    last INSCOPE;                last INSCOPE;
6495                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6496                            table => 1, html => 1,                !!!cp ('t293');
6497                           }->{$node->[1]}) {                last INSCOPE;
6498                    !!!cp ('t293');              }
6499                    last INSCOPE;            } # INSCOPE
6500                  }            unless (defined $i) {
6501                } # INSCOPE              !!!cp ('t294');
6502                unless (defined $i) {              ## Ignore the token
6503                  !!!cp ('t294');              !!!nack ('t294.1');
6504                  ## Ignore the token              !!!next-token;
6505                  !!!next-token;              next B;
6506                  redo B;            }
               }  
6507                                
6508                ## As if </select>            ## As if </select>
6509                ## have an element in table scope            ## have an element in table scope
6510                undef $i;            undef $i;
6511                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6512                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6513                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6514                    !!!cp ('t295');                !!!cp ('t295');
6515                    $i = $_;                $i = $_;
6516                    last INSCOPE;                last INSCOPE;
6517                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6518  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6519                    !!!cp ('t296');                !!!cp ('t296');
6520                    last INSCOPE;                last INSCOPE;
6521                  }              }
6522                } # INSCOPE            } # INSCOPE
6523                unless (defined $i) {            unless (defined $i) {
6524                  !!!cp ('t297');              !!!cp ('t297');
6525  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6526                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6527                  ## Ignore the </select> token                              text => 'select', token => $token);
6528                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6529                  redo B;              !!!nack ('t297.1');
6530                }              !!!next-token; ## TODO: ok?
6531                next B;
6532              }
6533                                
6534                !!!cp ('t298');            !!!cp ('t298');
6535                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6536    
6537                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6538    
6539                ## reprocess            !!!ack-later;
6540                redo B;            ## reprocess
6541              next B;
6542          } else {          } else {
6543            !!!cp ('t299');            !!!cp ('t299');
6544            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/',
6545                              text => $token->{tag_name}, token => $token);
6546            ## Ignore the token            ## Ignore the token
6547              !!!nack ('t299.3');
6548            !!!next-token;            !!!next-token;
6549            redo B;            next B;
6550          }          }
6551          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6552            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6553                    @{$self->{open_elements}} == 1) { # redundant, maybe
6554              !!!cp ('t299.1');
6555              !!!parse-error (type => 'in body:#eof', token => $token);
6556            } else {
6557              !!!cp ('t299.2');
6558            }
6559    
6560            ## Stop parsing.
6561            last B;
6562        } else {        } else {
6563          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6564        }        }
6565      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6566        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6567          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6568            my $data = $1;            my $data = $1;
6569            ## As if in body            ## As if in body
6570            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5125  sub _tree_construction_main ($) { Line 6574  sub _tree_construction_main ($) {
6574            unless (length $token->{data}) {            unless (length $token->{data}) {
6575              !!!cp ('t300');              !!!cp ('t300');
6576              !!!next-token;              !!!next-token;
6577              redo B;              next B;
6578            }            }
6579          }          }
6580                    
6581          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6582            !!!cp ('t301');            !!!cp ('t301');
6583            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6584              #
           ## Reprocess in the "after body" insertion mode.  
6585          } else {          } else {
6586            !!!cp ('t302');            !!!cp ('t302');
6587              ## "after body" insertion mode
6588              !!!parse-error (type => 'after body:#text', token => $token);
6589              #
6590          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6591    
6592          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6593          ## reprocess          ## reprocess
6594          redo B;          next B;
6595        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6596          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6597            !!!cp ('t303');            !!!cp ('t303');
6598            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6599                                        text => $token->{tag_name}, token => $token);
6600            ## Reprocess in the "after body" insertion mode.            #
6601          } else {          } else {
6602            !!!cp ('t304');            !!!cp ('t304');
6603              ## "after body" insertion mode
6604              !!!parse-error (type => 'after body',
6605                              text => $token->{tag_name}, token => $token);
6606              #
6607          }          }
6608    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6609          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6610            !!!ack-later;
6611          ## reprocess          ## reprocess
6612          redo B;          next B;
6613        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6614          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6615            !!!cp ('t305');            !!!cp ('t305');
6616            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/',
6617                              text => $token->{tag_name}, token => $token);
6618                        
6619            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6620            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6621              next B;
6622          } else {          } else {
6623            !!!cp ('t306');            !!!cp ('t306');
6624          }          }
# Line 5175  sub _tree_construction_main ($) { Line 6627  sub _tree_construction_main ($) {
6627          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6628            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6629              !!!cp ('t307');              !!!cp ('t307');
6630              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag',
6631                                text => 'html', token => $token);
6632              ## Ignore the token              ## Ignore the token
6633              !!!next-token;              !!!next-token;
6634              redo B;              next B;
6635            } else {            } else {
6636              !!!cp ('t308');              !!!cp ('t308');
6637              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6638              !!!next-token;              !!!next-token;
6639              redo B;              next B;
6640            }            }
6641          } else {          } else {
6642            !!!cp ('t309');            !!!cp ('t309');
6643            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/',
6644                              text => $token->{tag_name}, token => $token);
6645    
6646            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6647            ## reprocess            ## reprocess
6648            redo B;            next B;
6649          }          }
6650          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6651            !!!cp ('t309.2');
6652            ## Stop parsing
6653            last B;
6654        } else {        } else {
6655          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6656        }        }
6657      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6658        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6659          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6660            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6661                        
6662            unless (length $token->{data}) {            unless (length $token->{data}) {
6663              !!!cp ('t310');              !!!cp ('t310');
6664              !!!next-token;              !!!next-token;
6665              redo B;              next B;
6666            }            }
6667          }          }
6668                    
6669          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6670            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6671              !!!cp ('t311');              !!!cp ('t311');
6672              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6673            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6674              !!!cp ('t312');              !!!cp ('t312');
6675              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6676            } else { # "after html frameset"            } else { # "after after frameset"
6677              !!!cp ('t313');              !!!cp ('t313');
6678              !!!parse-error (type => 'after html:#character');              !!!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');  
6679            }            }
6680                        
6681            ## Ignore the token.            ## Ignore the token.
# Line 5232  sub _tree_construction_main ($) { Line 6686  sub _tree_construction_main ($) {
6686              !!!cp ('t315');              !!!cp ('t315');
6687              !!!next-token;              !!!next-token;
6688            }            }
6689            redo B;            next B;
6690          }          }
6691                    
6692          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6693        } 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});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t317');  
         }  
   
6694          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6695              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6696            !!!cp ('t318');            !!!cp ('t318');
6697            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6698              !!!nack ('t318.1');
6699            !!!next-token;            !!!next-token;
6700            redo B;            next B;
6701          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6702                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6703            !!!cp ('t319');            !!!cp ('t319');
6704            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6705            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6706              !!!ack ('t319.1');
6707            !!!next-token;            !!!next-token;
6708            redo B;            next B;
6709          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6710            !!!cp ('t320');            !!!cp ('t320');
6711            ## NOTE: As if in body.            ## NOTE: As if in head.
6712            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6713            redo B;            next B;
6714    
6715              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6716              ## has no parse error.
6717          } else {          } else {
6718            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6719              !!!cp ('t321');              !!!cp ('t321');
6720              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6721            } else {                              text => $token->{tag_name}, token => $token);
6722              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6723              !!!cp ('t322');              !!!cp ('t322');
6724              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset',
6725                                text => $token->{tag_name}, token => $token);
6726              } else { # "after after frameset"
6727                !!!cp ('t322.2');
6728                !!!parse-error (type => 'after after frameset',
6729                                text => $token->{tag_name}, token => $token);
6730            }            }
6731            ## Ignore the token            ## Ignore the token
6732              !!!nack ('t322.1');
6733            !!!next-token;            !!!next-token;
6734            redo B;            next B;
6735          }          }
6736        } 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});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "after frameset" insertion mode.  
         } else {  
           !!!cp ('t324');  
         }  
   
6737          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6738              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6739            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6740                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6741              !!!cp ('t325');              !!!cp ('t325');
6742              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6743                                text => $token->{tag_name}, token => $token);
6744              ## Ignore the token              ## Ignore the token
6745              !!!next-token;              !!!next-token;
6746            } else {            } else {
# Line 5303  sub _tree_construction_main ($) { Line 6750  sub _tree_construction_main ($) {
6750            }            }
6751    
6752            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6753                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6754              !!!cp ('t327');              !!!cp ('t327');
6755              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6756            } else {            } else {
6757              !!!cp ('t328');              !!!cp ('t328');
6758            }            }
6759            redo B;            next B;
6760          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6761                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6762            !!!cp ('t329');            !!!cp ('t329');
6763            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6764            !!!next-token;            !!!next-token;
6765            redo B;            next B;
6766          } else {          } else {
6767            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6768              !!!cp ('t330');              !!!cp ('t330');
6769              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6770            } else {                              text => $token->{tag_name}, token => $token);
6771              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6772                !!!cp ('t330.1');
6773                !!!parse-error (type => 'after frameset:/',
6774                                text => $token->{tag_name}, token => $token);
6775              } else { # "after after html"
6776              !!!cp ('t331');              !!!cp ('t331');
6777              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after after frameset:/',
6778                                text => $token->{tag_name}, token => $token);
6779            }            }
6780            ## Ignore the token            ## Ignore the token
6781            !!!next-token;            !!!next-token;
6782            redo B;            next B;
6783          }          }
6784          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6785            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6786                    @{$self->{open_elements}} == 1) { # redundant, maybe
6787              !!!cp ('t331.1');
6788              !!!parse-error (type => 'in body:#eof', token => $token);
6789            } else {
6790              !!!cp ('t331.2');
6791            }
6792            
6793            ## Stop parsing
6794            last B;
6795        } else {        } else {
6796          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6797        }        }
   
       ## ISSUE: An issue in spec here  
6798      } else {      } else {
6799        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6800      }      }
# Line 5343  sub _tree_construction_main ($) { Line 6805  sub _tree_construction_main ($) {
6805          !!!cp ('t332');          !!!cp ('t332');
6806          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6807          $script_start_tag->();          $script_start_tag->();
6808          redo B;          next B;
6809        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6810          !!!cp ('t333');          !!!cp ('t333');
6811          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6812          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6813          redo B;          next B;
6814        } elsif ({        } elsif ({
6815                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6816                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6817          !!!cp ('t334');          !!!cp ('t334');
6818          ## 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
6819          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6820          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6821            !!!ack ('t334.1');
6822          !!!next-token;          !!!next-token;
6823          redo B;          next B;
6824        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6825          ## 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
6826          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6827          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6828    
6829          unless ($self->{confident}) {          unless ($self->{confident}) {
6830            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6831              !!!cp ('t335');              !!!cp ('t335');
6832                ## NOTE: Whether the encoding is supported or not is handled
6833                ## in the {change_encoding} callback.
6834              $self->{change_encoding}              $self->{change_encoding}
6835                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6836                            
6837              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6838                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6839                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6840                                           ->{has_reference});                                           ->{has_reference});
6841            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6842              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6843                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6844                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6845                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6846                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6847                       /x) {
6848                !!!cp ('t336');                !!!cp ('t336');
6849                  ## NOTE: Whether the encoding is supported or not is handled
6850                  ## in the {change_encoding} callback.
6851                $self->{change_encoding}                $self->{change_encoding}
6852                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6853                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6854                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6855                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5406  sub _tree_construction_main ($) { Line 6873  sub _tree_construction_main ($) {
6873            }            }
6874          }          }
6875    
6876            !!!ack ('t338.1');
6877          !!!next-token;          !!!next-token;
6878          redo B;          next B;
6879        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6880          !!!cp ('t341');          !!!cp ('t341');
6881          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6882          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6883          redo B;          next B;
6884        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6885          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6886                                
6887          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6888              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6889            !!!cp ('t342');            !!!cp ('t342');
6890            ## Ignore the token            ## Ignore the token
6891          } else {          } else {
# Line 5431  sub _tree_construction_main ($) { Line 6899  sub _tree_construction_main ($) {
6899              }              }
6900            }            }
6901          }          }
6902            !!!nack ('t343.1');
6903          !!!next-token;          !!!next-token;
6904          redo B;          next B;
6905        } elsif ({        } elsif ({
6906                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6907                  div => 1, dl => 1, fieldset => 1,  
6908                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6909                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6910                    center => 1, datagrid => 1, details => 1, dialog => 1,
6911                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6912                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6913                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6914                    section => 1, ul => 1,
6915                    ## NOTE: As normal, but drops leading newline
6916                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6917                    ## NOTE: As normal, but interacts with the form element pointer
6918                    form => 1,
6919                    
6920                    table => 1,
6921                    hr => 1,
6922                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6923            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6924              !!!cp ('t350');
6925              !!!parse-error (type => 'in form:form', token => $token);
6926              ## Ignore the token
6927              !!!nack ('t350.1');
6928              !!!next-token;
6929              next B;
6930            }
6931    
6932          ## has a p element in scope          ## has a p element in scope
6933          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6934            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6935              !!!cp ('t344');              !!!cp ('t344');
6936              !!!back-token;              !!!back-token; # <form>
6937              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6938              redo B;                        line => $token->{line}, column => $token->{column}};
6939            } elsif ({              next B;
6940                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6941              !!!cp ('t345');              !!!cp ('t345');
6942              last INSCOPE;              last INSCOPE;
6943            }            }
6944          } # INSCOPE          } # INSCOPE
6945                        
6946          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6947          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6948              !!!nack ('t346.1');
6949            !!!next-token;            !!!next-token;
6950            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6951              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5470  sub _tree_construction_main ($) { Line 6958  sub _tree_construction_main ($) {
6958            } else {            } else {
6959              !!!cp ('t348');              !!!cp ('t348');
6960            }            }
6961          } else {          } elsif ($token->{tag_name} eq 'form') {
6962            !!!cp ('t347');            !!!cp ('t347.1');
6963              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6964    
6965              !!!nack ('t347.2');
6966            !!!next-token;            !!!next-token;
6967          }          } elsif ($token->{tag_name} eq 'table') {
6968          redo B;            !!!cp ('t382');
6969        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6970          if (defined $self->{form_element}) {            
6971            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
6972            !!!parse-error (type => 'in form:form');  
6973            ## Ignore the token            !!!nack ('t382.1');
6974              !!!next-token;
6975            } elsif ($token->{tag_name} eq 'hr') {
6976              !!!cp ('t386');
6977              pop @{$self->{open_elements}};
6978            
6979              !!!nack ('t386.1');
6980            !!!next-token;            !!!next-token;
           redo B;  
6981          } else {          } else {
6982            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!cp ('t351');  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
               redo B;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               !!!cp ('t352');  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
6983            !!!next-token;            !!!next-token;
           redo B;  
6984          }          }
6985            next B;
6986        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
6987          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
6988          INSCOPE: for (reverse @{$self->{open_elements}}) {  
6989            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6990              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
6991              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6992              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6993              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6994            } elsif ({            ## object (Fx)
6995                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
6996                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
6997                     }->{$_->[1]}) {            ## form (IE), hn (IE)
6998              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
6999              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7000            }            ## div (Fx, S)
7001          } # INSCOPE  
7002                      my $non_optional;
         ## Step 1  
7003          my $i = -1;          my $i = -1;
7004          my $node = $self->{open_elements}->[$i];  
7005          LI: {          ## 1.
7006            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7007            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7008              if ($i != -1) {              ## 2. (a) As if </li>
7009                !!!cp ('t355');              {
7010                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7011                                $self->{open_elements}->[-1]->[1]);                #
7012              } else {  
7013                !!!cp ('t356');                ## Otherwise
7014    
7015                  ## 1. generate implied end tags, except for </li>
7016                  #
7017    
7018                  ## 2. If current node != "li", parse error
7019                  if ($non_optional) {
7020                    !!!parse-error (type => 'not closed',
7021                                    text => $non_optional->[0]->manakai_local_name,
7022                                    token => $token);
7023                    !!!cp ('t355');
7024                  } else {
7025                    !!!cp ('t356');
7026                  }
7027    
7028                  ## 3. Pop
7029                  splice @{$self->{open_elements}}, $i;
7030              }              }
7031              splice @{$self->{open_elements}}, $i;  
7032              last LI;              last; ## 2. (b) goto 5.
7033            } else {            } elsif (
7034                       ## NOTE: not "formatting" and not "phrasing"
7035                       ($node->[1] & SPECIAL_EL or
7036                        $node->[1] & SCOPING_EL) and
7037                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7038    
7039                       (not $node->[1] & ADDRESS_EL) &
7040                       (not $node->[1] & DIV_EL) &
7041                       (not $node->[1] & P_EL)) {
7042                ## 3.
7043              !!!cp ('t357');              !!!cp ('t357');
7044            }              last; ## goto 5.
7045                        } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
           ## 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') {  
7046              !!!cp ('t358');              !!!cp ('t358');
7047              last LI;              #
7048              } else {
7049                !!!cp ('t359');
7050                $non_optional ||= $node;
7051                #
7052            }            }
7053                        ## 4.
7054            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7055            $i--;            $i--;
7056            $node = $self->{open_elements}->[$i];          }
7057            redo LI;  
7058          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
7059          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7060            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7061              !!!cp ('t360');              !!!cp ('t353');
7062              !!!back-token;  
7063              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7064              redo B;  
7065            } elsif ({              !!!back-token; # <x>
7066                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7067                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7068                     }->{$_->[1]}) {              next B;
7069              !!!cp ('t361');            } elsif ($_->[1] & SCOPING_EL) {
7070                !!!cp ('t354');
7071              last INSCOPE;              last INSCOPE;
7072            }            }
7073          } # INSCOPE          } # INSCOPE
7074              
7075          ## Step 1          ## 5. (b) insert
7076            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7077            !!!nack ('t359.1');
7078            !!!next-token;
7079            next B;
7080          } elsif ($token->{tag_name} eq 'dt' or
7081                   $token->{tag_name} eq 'dd') {
7082            ## NOTE: As normal, but imply </dt> or </dd> when ...
7083    
7084            my $non_optional;
7085          my $i = -1;          my $i = -1;
7086          my $node = $self->{open_elements}->[$i];  
7087          LI: {          ## 1.
7088            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7089            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7090              if ($i != -1) {              ## 2. (a) As if </li>
7091                !!!cp ('t362');              {
7092                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7093                                $self->{open_elements}->[-1]->[1]);                #
7094              } else {  
7095                !!!cp ('t363');                ## Otherwise
7096    
7097                  ## 1. generate implied end tags, except for </dt> or </dd>
7098                  #
7099    
7100                  ## 2. If current node != "dt"|"dd", parse error
7101                  if ($non_optional) {
7102                    !!!parse-error (type => 'not closed',
7103                                    text => $non_optional->[0]->manakai_local_name,
7104                                    token => $token);
7105                    !!!cp ('t355.1');
7106                  } else {
7107                    !!!cp ('t356.1');
7108                  }
7109    
7110                  ## 3. Pop
7111                  splice @{$self->{open_elements}}, $i;
7112              }              }
7113              splice @{$self->{open_elements}}, $i;  
7114              last LI;              last; ## 2. (b) goto 5.
7115              } elsif (
7116                       ## NOTE: not "formatting" and not "phrasing"
7117                       ($node->[1] & SPECIAL_EL or
7118                        $node->[1] & SCOPING_EL) and
7119                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7120    
7121                       (not $node->[1] & ADDRESS_EL) &
7122                       (not $node->[1] & DIV_EL) &
7123                       (not $node->[1] & P_EL)) {
7124                ## 3.
7125                !!!cp ('t357.1');
7126                last; ## goto 5.
7127              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7128                !!!cp ('t358.1');
7129                #
7130            } else {            } else {
7131              !!!cp ('t364');              !!!cp ('t359.1');
7132            }              $non_optional ||= $node;
7133                          #
           ## 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 ('t365');  
             last LI;  
7134            }            }
7135                        ## 4.
7136            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7137            $i--;            $i--;
7138            $node = $self->{open_elements}->[$i];          }
7139            redo LI;  
7140          } # LI          ## 5. (a) has a |p| element in scope
7141                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7142          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] & P_EL) {
7143                !!!cp ('t353.1');
7144                !!!back-token; # <x>
7145                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7146                          line => $token->{line}, column => $token->{column}};
7147                next B;
7148              } elsif ($_->[1] & SCOPING_EL) {
7149                !!!cp ('t354.1');
7150                last INSCOPE;
7151              }
7152            } # INSCOPE
7153    
7154            ## 5. (b) insert
7155            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7156            !!!nack ('t359.2');
7157          !!!next-token;          !!!next-token;
7158          redo B;          next B;
7159        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7160            ## NOTE: As normal, but effectively ends parsing
7161    
7162          ## has a p element in scope          ## has a p element in scope
7163          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7164            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7165              !!!cp ('t367');              !!!cp ('t367');
7166              !!!back-token;              !!!back-token; # <plaintext>
7167              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7168              redo B;                        line => $token->{line}, column => $token->{column}};
7169            } elsif ({              next B;
7170                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7171              !!!cp ('t368');              !!!cp ('t368');
7172              last INSCOPE;              last INSCOPE;
7173            }            }
7174          } # INSCOPE          } # INSCOPE
7175                        
7176          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7177                        
7178          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7179                        
7180            !!!nack ('t368.1');
7181          !!!next-token;          !!!next-token;
7182          redo B;          next B;
7183        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7184          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7185            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7186            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7187              !!!cp ('t371');              !!!cp ('t371');
7188              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
7189                            
7190              !!!back-token;              !!!back-token; # <a>
7191              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7192              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7193                $formatting_end_tag->($token);
7194                            
7195              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7196                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5673  sub _tree_construction_main ($) { Line 7215  sub _tree_construction_main ($) {
7215                        
7216          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7217    
7218          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7219          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7220    
7221            !!!nack ('t374.1');
7222          !!!next-token;          !!!next-token;
7223          redo B;          next B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         !!!cp ('t375');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         redo B;  
7224        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7225          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7226    
7227          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7228          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7229            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7230            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7231              !!!cp ('t376');              !!!cp ('t376');
7232              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7233              !!!back-token;              !!!back-token; # <nobr>
7234              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7235              redo B;                        line => $token->{line}, column => $token->{column}};
7236            } elsif ({              next B;
7237                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7238              !!!cp ('t377');              !!!cp ('t377');
7239              last INSCOPE;              last INSCOPE;
7240            }            }
7241          } # INSCOPE          } # INSCOPE
7242                    
7243          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7244          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7245                    
7246            !!!nack ('t377.1');
7247          !!!next-token;          !!!next-token;
7248          redo B;          next B;
7249        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7250          ## has a button element in scope          ## has a button element in scope
7251          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7252            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7253            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7254              !!!cp ('t378');              !!!cp ('t378');
7255              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7256              !!!back-token;              !!!back-token; # <button>
7257              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7258              redo B;                        line => $token->{line}, column => $token->{column}};
7259            } elsif ({              next B;
7260                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7261              !!!cp ('t379');              !!!cp ('t379');
7262              last INSCOPE;              last INSCOPE;
7263            }            }
# Line 5738  sub _tree_construction_main ($) { Line 7265  sub _tree_construction_main ($) {
7265                        
7266          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7267                        
7268          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7269    
7270          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7271    
7272          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7273    
7274            !!!nack ('t379.1');
7275          !!!next-token;          !!!next-token;
7276          redo B;          next B;
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         !!!cp ('t380');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         !!!cp ('t381');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!cp ('t382');  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             !!!cp ('t383');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];  
   
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo B;  
7277        } elsif ({        } elsif ({
7278                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7279                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7280                  image => 1,                  noembed => 1,
7281                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7282                    noscript => 0, ## TODO: 1 if scripting is enabled
7283                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7284          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7285            !!!cp ('t384');            !!!cp ('t381');
7286            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7287          } else {          } else {
7288            !!!cp ('t385');            !!!cp ('t399');
7289          }          }
7290            ## NOTE: There is an "as if in body" code clone.
7291          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7292          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!cp ('t386');  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             !!!cp ('t387');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'input') {  
         !!!cp ('t388');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
7293        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7294          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7295                    
7296          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7297            !!!cp ('t389');            !!!cp ('t389');
7298            ## Ignore the token            ## Ignore the token
7299              !!!nack ('t389'); ## NOTE: Not acknowledged.
7300            !!!next-token;            !!!next-token;
7301            redo B;            next B;
7302          } else {          } else {
7303              !!!ack ('t391.1');
7304    
7305            my $at = $token->{attributes};            my $at = $token->{attributes};
7306            my $form_attrs;            my $form_attrs;
7307            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5856  sub _tree_construction_main ($) { Line 7311  sub _tree_construction_main ($) {
7311            delete $at->{prompt};            delete $at->{prompt};
7312            my @tokens = (            my @tokens = (
7313                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7314                           attributes => $form_attrs},                           attributes => $form_attrs,
7315                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7316                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7317                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7318                            {type => START_TAG_TOKEN, tag_name => 'p',
7319                             line => $token->{line}, column => $token->{column}},
7320                            {type => START_TAG_TOKEN, tag_name => 'label',
7321                             line => $token->{line}, column => $token->{column}},
7322                         );                         );
7323            if ($prompt_attr) {            if ($prompt_attr) {
7324              !!!cp ('t390');              !!!cp ('t390');
7325              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7326                               #line => $token->{line}, column => $token->{column},
7327                              };
7328            } else {            } else {
7329              !!!cp ('t391');              !!!cp ('t391');
7330              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7331                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7332                               #line => $token->{line}, column => $token->{column},
7333                              }; # SHOULD
7334              ## TODO: make this configurable              ## TODO: make this configurable
7335            }            }
7336            push @tokens,            push @tokens,
7337                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7338                             line => $token->{line}, column => $token->{column}},
7339                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7340                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7341                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7342                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7343                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7344            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7345                             line => $token->{line}, column => $token->{column}},
7346                            {type => END_TAG_TOKEN, tag_name => 'form',
7347                             line => $token->{line}, column => $token->{column}};
7348            !!!back-token (@tokens);            !!!back-token (@tokens);
7349            redo B;            !!!next-token;
7350              next B;
7351          }          }
7352        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7353          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7354          my $el;          my $el;
7355          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7356                    
7357          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7358          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5893  sub _tree_construction_main ($) { Line 7361  sub _tree_construction_main ($) {
7361          $insert->($el);          $insert->($el);
7362                    
7363          my $text = '';          my $text = '';
7364            !!!nack ('t392.1');
7365          !!!next-token;          !!!next-token;
7366          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7367            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 5923  sub _tree_construction_main ($) { Line 7392  sub _tree_construction_main ($) {
7392            ## Ignore the token            ## Ignore the token
7393          } else {          } else {
7394            !!!cp ('t398');            !!!cp ('t398');
7395            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7396          }          }
7397          !!!next-token;          !!!next-token;
7398            next B;
7399          } elsif ($token->{tag_name} eq 'rt' or
7400                   $token->{tag_name} eq 'rp') {
7401            ## has a |ruby| element in scope
7402            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7403              my $node = $self->{open_elements}->[$_];
7404              if ($node->[1] & RUBY_EL) {
7405                !!!cp ('t398.1');
7406                ## generate implied end tags
7407                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7408                  !!!cp ('t398.2');
7409                  pop @{$self->{open_elements}};
7410                }
7411                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7412                  !!!cp ('t398.3');
7413                  !!!parse-error (type => 'not closed',
7414                                  text => $self->{open_elements}->[-1]->[0]
7415                                      ->manakai_local_name,
7416                                  token => $token);
7417                  pop @{$self->{open_elements}}
7418                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7419                }
7420                last INSCOPE;
7421              } elsif ($node->[1] & SCOPING_EL) {
7422                !!!cp ('t398.4');
7423                last INSCOPE;
7424              }
7425            } # INSCOPE
7426    
7427            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7428    
7429            !!!nack ('t398.5');
7430            !!!next-token;
7431          redo B;          redo B;
7432        } elsif ({        } elsif ($token->{tag_name} eq 'math' or
7433                  iframe => 1,                 $token->{tag_name} eq 'svg') {
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t399');  
         ## NOTE: There is an "as if in body" code clone.  
         $parse_rcdata->(CDATA_CONTENT_MODEL);  
         redo B;  
       } elsif ($token->{tag_name} eq 'select') {  
         !!!cp ('t400');  
7434          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7435    
7436          ## TODO: associate with $self->{form_element} if defined          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7437    
7438            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7439    
7440            ## "adjust foreign attributes" - done in insert-element-f
7441                    
7442          $self->{insertion_mode} = IN_SELECT_IM;          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7443            
7444            if ($self->{self_closing}) {
7445              pop @{$self->{open_elements}};
7446              !!!ack ('t398.1');
7447            } else {
7448              !!!cp ('t398.2');
7449              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7450              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7451              ## mode, "in body" (not "in foreign content") secondary insertion
7452              ## mode, maybe.
7453            }
7454    
7455          !!!next-token;          !!!next-token;
7456          redo B;          next B;
7457        } elsif ({        } elsif ({
7458                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7459                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 5955  sub _tree_construction_main ($) { Line 7461  sub _tree_construction_main ($) {
7461                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7462                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7463          !!!cp ('t401');          !!!cp ('t401');
7464          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body',
7465                            text => $token->{tag_name}, token => $token);
7466          ## Ignore the token          ## Ignore the token
7467            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7468            !!!next-token;
7469            next B;
7470          } elsif ($token->{tag_name} eq 'param' or
7471                   $token->{tag_name} eq 'source') {
7472            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7473            pop @{$self->{open_elements}};
7474    
7475            !!!ack ('t398.5');
7476          !!!next-token;          !!!next-token;
7477          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7478        } else {        } else {
7479          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
7480              !!!cp ('t384');
7481              !!!parse-error (type => 'image', token => $token);
7482              $token->{tag_name} = 'img';
7483            } else {
7484              !!!cp ('t385');
7485            }
7486    
7487            ## NOTE: There is an "as if <br>" code clone.
7488          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7489                    
7490          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7491    
7492            if ({
7493                 applet => 1, marquee => 1, object => 1,
7494                }->{$token->{tag_name}}) {
7495              !!!cp ('t380');
7496              push @$active_formatting_elements, ['#marker', ''];
7497              !!!nack ('t380.1');
7498            } elsif ({
7499                      b => 1, big => 1, em => 1, font => 1, i => 1,
7500                      s => 1, small => 1, strike => 1,
7501                      strong => 1, tt => 1, u => 1,
7502                     }->{$token->{tag_name}}) {
7503              !!!cp ('t375');
7504              push @$active_formatting_elements, $self->{open_elements}->[-1];
7505              !!!nack ('t375.1');
7506            } elsif ($token->{tag_name} eq 'input') {
7507              !!!cp ('t388');
7508              ## TODO: associate with $self->{form_element} if defined
7509              pop @{$self->{open_elements}};
7510              !!!ack ('t388.2');
7511            } elsif ({
7512                      area => 1, basefont => 1, bgsound => 1, br => 1,
7513                      embed => 1, img => 1, spacer => 1, wbr => 1,
7514                     }->{$token->{tag_name}}) {
7515              !!!cp ('t388.1');
7516              pop @{$self->{open_elements}};
7517              !!!ack ('t388.3');
7518            } elsif ($token->{tag_name} eq 'select') {
7519              ## TODO: associate with $self->{form_element} if defined
7520            
7521              if ($self->{insertion_mode} & TABLE_IMS or
7522                  $self->{insertion_mode} & BODY_TABLE_IMS or
7523                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7524                !!!cp ('t400.1');
7525                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7526              } else {
7527                !!!cp ('t400.2');
7528                $self->{insertion_mode} = IN_SELECT_IM;
7529              }
7530              !!!nack ('t400.3');
7531            } else {
7532              !!!nack ('t402');
7533            }
7534                    
7535          !!!next-token;          !!!next-token;
7536          redo B;          next B;
7537        }        }
7538      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7539        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7540          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7541              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7542            for (@{$self->{open_elements}}) {          INSCOPE: {
7543              unless ({            for (reverse @{$self->{open_elements}}) {
7544                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7545                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7546                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7547                      }->{$_->[1]}) {                last INSCOPE;
7548                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7549                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7550              } else {                last;
               !!!cp ('t404');  
7551              }              }
7552            }            }
7553    
7554            $self->{insertion_mode} = AFTER_BODY_IM;            !!!parse-error (type => 'start tag not allowed',
7555                              text => $token->{tag_name}, token => $token);
7556              ## NOTE: Ignore the token.
7557            !!!next-token;            !!!next-token;
7558            redo B;            next B;
7559          } else {          } # INSCOPE
7560            !!!cp ('t405');  
7561            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          for (@{$self->{open_elements}}) {
7562            ## Ignore the token            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7563            !!!next-token;              !!!cp ('t403');
7564            redo B;              !!!parse-error (type => 'not closed',
7565                                text => $_->[0]->manakai_local_name,
7566                                token => $token);
7567                last;
7568              } else {
7569                !!!cp ('t404');
7570              }
7571          }          }
7572    
7573            $self->{insertion_mode} = AFTER_BODY_IM;
7574            !!!next-token;
7575            next B;
7576        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7577          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
7578            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7579            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7580                $self->{open_elements}->[1]->[1] & BODY_EL) {
7581              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7582              !!!cp ('t406');              !!!cp ('t406');
7583              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
7584                                text => $self->{open_elements}->[1]->[0]
7585                                    ->manakai_local_name,
7586                                token => $token);
7587            } else {            } else {
7588              !!!cp ('t407');              !!!cp ('t407');
7589            }            }
7590            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7591            ## reprocess            ## reprocess
7592            redo B;            next B;
7593          } else {          } else {
7594            !!!cp ('t408');            !!!cp ('t408');
7595            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7596                              text => $token->{tag_name}, token => $token);
7597            ## Ignore the token            ## Ignore the token
7598            !!!next-token;            !!!next-token;
7599            redo B;            next B;
7600          }          }
7601        } elsif ({        } elsif ({
7602                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7603                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7604                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7605                    address => 1, article => 1, aside => 1, blockquote => 1,
7606                    center => 1, datagrid => 1, details => 1, dialog => 1,
7607                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7608                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7609                    ol => 1, pre => 1, section => 1, ul => 1,
7610    
7611                    ## NOTE: As normal, but ... optional tags
7612                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7613                  button => 1, marquee => 1, object => 1,  
7614                    applet => 1, button => 1, marquee => 1, object => 1,
7615                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7616            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7617            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7618            ## </dd>" code.
7619    
7620          ## has an element in scope          ## has an element in scope
7621          my $i;          my $i;
7622          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7623            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7624            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7625              !!!cp ('t410');              !!!cp ('t410');
7626              $i = $_;              $i = $_;
7627              last INSCOPE;              last INSCOPE;
7628            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7629              !!!cp ('t411');              !!!cp ('t411');
7630              last INSCOPE;              last INSCOPE;
7631            }            }
# Line 6042  sub _tree_construction_main ($) { Line 7633  sub _tree_construction_main ($) {
7633    
7634          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7635            !!!cp ('t413');            !!!cp ('t413');
7636            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7637                              text => $token->{tag_name}, token => $token);
7638              ## NOTE: Ignore the token.
7639          } else {          } else {
7640            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7641            while ({            while ({
7642                      ## END_TAG_OPTIONAL_EL
7643                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7644                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7645                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7646                      option => 1,
7647                      optgroup => 1,
7648                    p => 1,                    p => 1,
7649                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7650                      rp => 1,
7651                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7652              !!!cp ('t409');              !!!cp ('t409');
7653              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7654            }            }
7655    
7656            ## Step 2.            ## Step 2.
7657            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7658                      ne $token->{tag_name}) {
7659              !!!cp ('t412');              !!!cp ('t412');
7660              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7661                                text => $self->{open_elements}->[-1]->[0]
7662                                    ->manakai_local_name,
7663                                token => $token);
7664            } else {            } else {
7665              !!!cp ('t414');              !!!cp ('t414');
7666            }            }
# Line 6069  sub _tree_construction_main ($) { Line 7671  sub _tree_construction_main ($) {
7671            ## Step 4.            ## Step 4.
7672            $clear_up_to_marker->()            $clear_up_to_marker->()
7673                if {                if {
7674                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7675                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7676          }          }
7677          !!!next-token;          !!!next-token;
7678          redo B;          next B;
7679        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7680            ## NOTE: As normal, but interacts with the form element pointer
7681    
7682          undef $self->{form_element};          undef $self->{form_element};
7683    
7684          ## has an element in scope          ## has an element in scope
7685          my $i;          my $i;
7686          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7687            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7688            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7689              !!!cp ('t418');              !!!cp ('t418');
7690              $i = $_;              $i = $_;
7691              last INSCOPE;              last INSCOPE;
7692            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7693              !!!cp ('t419');              !!!cp ('t419');
7694              last INSCOPE;              last INSCOPE;
7695            }            }
# Line 6096  sub _tree_construction_main ($) { Line 7697  sub _tree_construction_main ($) {
7697    
7698          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7699            !!!cp ('t421');            !!!cp ('t421');
7700            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7701                              text => $token->{tag_name}, token => $token);
7702              ## NOTE: Ignore the token.
7703          } else {          } else {
7704            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7705            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7706              !!!cp ('t417');              !!!cp ('t417');
7707              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7708            }            }
7709                        
7710            ## Step 2.            ## Step 2.
7711            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7712                      ne $token->{tag_name}) {
7713              !!!cp ('t417.1');              !!!cp ('t417.1');
7714              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7715                                text => $self->{open_elements}->[-1]->[0]
7716                                    ->manakai_local_name,
7717                                token => $token);
7718            } else {            } else {
7719              !!!cp ('t420');              !!!cp ('t420');
7720            }              }  
# Line 6119  sub _tree_construction_main ($) { Line 7724  sub _tree_construction_main ($) {
7724          }          }
7725    
7726          !!!next-token;          !!!next-token;
7727          redo B;          next B;
7728        } elsif ({        } elsif ({
7729                    ## NOTE: As normal, except acts as a closer for any ...
7730                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7731                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7732          ## has an element in scope          ## has an element in scope
7733          my $i;          my $i;
7734          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7735            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7736            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7737              !!!cp ('t423');              !!!cp ('t423');
7738              $i = $_;              $i = $_;
7739              last INSCOPE;              last INSCOPE;
7740            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7741              !!!cp ('t424');              !!!cp ('t424');
7742              last INSCOPE;              last INSCOPE;
7743            }            }
# Line 6144  sub _tree_construction_main ($) { Line 7745  sub _tree_construction_main ($) {
7745    
7746          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7747            !!!cp ('t425.1');            !!!cp ('t425.1');
7748            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7749                              text => $token->{tag_name}, token => $token);
7750              ## NOTE: Ignore the token.
7751          } else {          } else {
7752            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7753            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7754              !!!cp ('t422');              !!!cp ('t422');
7755              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7756            }            }
7757                        
7758            ## Step 2.            ## Step 2.
7759            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7760                      ne $token->{tag_name}) {
7761              !!!cp ('t425');              !!!cp ('t425');
7762              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
7763                                text => $token->{tag_name}, token => $token);
7764            } else {            } else {
7765              !!!cp ('t426');              !!!cp ('t426');
7766            }            }
# Line 6167  sub _tree_construction_main ($) { Line 7770  sub _tree_construction_main ($) {
7770          }          }
7771                    
7772          !!!next-token;          !!!next-token;
7773          redo B;          next B;
7774        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7775            ## NOTE: As normal, except </p> implies <p> and ...
7776    
7777          ## has an element in scope          ## has an element in scope
7778            my $non_optional;
7779          my $i;          my $i;
7780          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7781            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7782            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7783              !!!cp ('t410.1');              !!!cp ('t410.1');
7784              $i = $_;              $i = $_;
7785              last INSCOPE;              last INSCOPE;
7786            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7787              !!!cp ('t411.1');              !!!cp ('t411.1');
7788              last INSCOPE;              last INSCOPE;
7789              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7790                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7791                !!!cp ('t411.2');
7792                #
7793              } else {
7794                !!!cp ('t411.3');
7795                $non_optional ||= $node;
7796                #
7797            }            }
7798          } # INSCOPE          } # INSCOPE
7799    
7800          if (defined $i) {          if (defined $i) {
7801            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7802              #
7803    
7804              ## 2. If current node != "p", parse error
7805              if ($non_optional) {
7806              !!!cp ('t412.1');              !!!cp ('t412.1');
7807              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7808                                text => $non_optional->[0]->manakai_local_name,
7809                                token => $token);
7810            } else {            } else {
7811              !!!cp ('t414.1');              !!!cp ('t414.1');
7812            }            }
7813    
7814              ## 3. Pop
7815            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7816          } else {          } else {
7817            !!!cp ('t413.1');            !!!cp ('t413.1');
7818            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7819                              text => $token->{tag_name}, token => $token);
7820    
7821            !!!cp ('t415.1');            !!!cp ('t415.1');
7822            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7823            my $el;            my $el;
7824            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7825            $insert->($el);            $insert->($el);
7826            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7827          }          }
7828    
7829          !!!next-token;          !!!next-token;
7830          redo B;          next B;
7831        } elsif ({        } elsif ({
7832                  a => 1,                  a => 1,
7833                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7834                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7835                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7836                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7837          !!!cp ('t427');          !!!cp ('t427');
7838          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7839          redo B;          next B;
7840        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7841          !!!cp ('t428');          !!!cp ('t428');
7842          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag',
7843                            text => 'br', token => $token);
7844    
7845          ## As if <br>          ## As if <br>
7846          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7847                    
7848          my $el;          my $el;
7849          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7850          $insert->($el);          $insert->($el);
7851                    
7852          ## Ignore the token.          ## Ignore the token.
7853          !!!next-token;          !!!next-token;
7854          redo B;          next B;
7855        } elsif ({        } elsif ({
7856                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7857                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1, option => 1, optgroup => 1,
# Line 6245  sub _tree_construction_main ($) { Line 7865  sub _tree_construction_main ($) {
7865                  noscript => 0, ## TODO: if scripting is enabled                  noscript => 0, ## TODO: if scripting is enabled
7866                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7867          !!!cp ('t429');          !!!cp ('t429');
7868          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag',
7869                            text => $token->{tag_name}, token => $token);
7870          ## Ignore the token          ## Ignore the token
7871          !!!next-token;          !!!next-token;
7872          redo B;          next B;
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7873        } else {        } else {
7874            if ($token->{tag_name} eq 'sarcasm') {
7875              sleep 0.001; # take a deep breath
7876            }
7877    
7878          ## Step 1          ## Step 1
7879          my $node_i = -1;          my $node_i = -1;
7880          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7881    
7882          ## Step 2          ## Step 2
7883          S2: {          S2: {
7884            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7885              ## Step 1              ## Step 1
7886              ## generate implied end tags              ## generate implied end tags
7887              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7888                !!!cp ('t430');                !!!cp ('t430');
7889                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7890                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7891                  ## which seems wrong.
7892                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7893                  $node_i++;
7894              }              }
7895                    
7896              ## Step 2              ## Step 2
7897              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7898                        ne $token->{tag_name}) {
7899                !!!cp ('t431');                !!!cp ('t431');
7900                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7901                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7902                                  text => $self->{open_elements}->[-1]->[0]
7903                                      ->manakai_local_name,
7904                                  token => $token);
7905              } else {              } else {
7906                !!!cp ('t432');                !!!cp ('t432');
7907              }              }
7908                            
7909              ## Step 3              ## Step 3
7910              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7911    
7912              !!!next-token;              !!!next-token;
7913              last S2;              last S2;
7914            } else {            } else {
7915              ## Step 3              ## Step 3
7916              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7917                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7918                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7919                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7920                !!!cp ('t433');                !!!cp ('t433');
7921                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
7922                                  text => $token->{tag_name}, token => $token);
7923                ## Ignore the token                ## Ignore the token
7924                !!!next-token;                !!!next-token;
7925                last S2;                last S2;
             }  
7926    
7927                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7928                  ## 9.27, "a" is a child of <dd> (conforming).  In
7929                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7930                  ## "a" is a child of both <body> and <dd>.
7931                }
7932                
7933              !!!cp ('t434');              !!!cp ('t434');
7934            }            }
7935                        
# Line 6307  sub _tree_construction_main ($) { Line 7940  sub _tree_construction_main ($) {
7940            ## Step 5;            ## Step 5;
7941            redo S2;            redo S2;
7942          } # S2          } # S2
7943          redo B;          next B;
7944        }        }
7945      }      }
7946      redo B;      next B;
7947      } continue { # B
7948        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7949          ## NOTE: The code below is executed in cases where it does not have
7950          ## to be, but it it is harmless even in those cases.
7951          ## has an element in scope
7952          INSCOPE: {
7953            for (reverse 0..$#{$self->{open_elements}}) {
7954              my $node = $self->{open_elements}->[$_];
7955              if ($node->[1] & FOREIGN_EL) {
7956                last INSCOPE;
7957              } elsif ($node->[1] & SCOPING_EL) {
7958                last;
7959              }
7960            }
7961            
7962            ## NOTE: No foreign element in scope.
7963            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7964          } # INSCOPE
7965        }
7966    } # B    } # B
7967    
7968    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6318  sub _tree_construction_main ($) { Line 7970  sub _tree_construction_main ($) {
7970    ## TODO: script stuffs    ## TODO: script stuffs
7971  } # _tree_construct_main  } # _tree_construct_main
7972    
7973  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7974    my $class = shift;    my $class = shift;
7975    my $node = shift;    my $node = shift;
7976    my $s = \$_[0];    #my $s = \$_[0];
7977    my $onerror = $_[1];    my $onerror = $_[1];
7978      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7979    
7980    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
7981    
# Line 6341  sub set_inner_html ($$$) { Line 7994  sub set_inner_html ($$$) {
7994      }      }
7995    
7996      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7997      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7998    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7999      ## TODO: If non-html element      ## TODO: If non-html element
8000    
8001      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8002    
8003    ## TODO: Support for $get_wrapper
8004    
8005      ## Step 1 # MUST      ## Step 1 # MUST
8006      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8007      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6356  sub set_inner_html ($$$) { Line 8011  sub set_inner_html ($$$) {
8011    
8012      ## Step 8 # MUST      ## Step 8 # MUST
8013      my $i = 0;      my $i = 0;
8014      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8015      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8016      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8017        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8018        $input = $get_wrapper->($input);
8019        $p->{set_nc} = sub {
8020        my $self = shift;        my $self = shift;
8021    
8022        pop @{$self->{prev_char}};        my $char = '';
8023        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8024            $char = $self->{next_nc};
8025            delete $self->{next_nc};
8026            $self->{nc} = ord $char;
8027          } else {
8028            $self->{char_buffer} = '';
8029            $self->{char_buffer_pos} = 0;
8030            
8031            my $count = $input->manakai_read_until
8032                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8033                 $self->{char_buffer_pos});
8034            if ($count) {
8035              $self->{line_prev} = $self->{line};
8036              $self->{column_prev} = $self->{column};
8037              $self->{column}++;
8038              $self->{nc}
8039                  = ord substr ($self->{char_buffer},
8040                                $self->{char_buffer_pos}++, 1);
8041              return;
8042            }
8043            
8044            if ($input->read ($char, 1)) {
8045              $self->{nc} = ord $char;
8046            } else {
8047              $self->{nc} = -1;
8048              return;
8049            }
8050          }
8051    
8052        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8053        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8054        $column++;  
8055          if ($self->{nc} == 0x000A) { # LF
8056        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8057          $line++;          $p->{column} = 0;
         $column = 0;  
8058          !!!cp ('i1');          !!!cp ('i1');
8059        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8060          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8061          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8062          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8063          $column = 0;            $self->{next_nc} = $next;
8064            }
8065            $self->{nc} = 0x000A; # LF # MUST
8066            $p->{line}++;
8067            $p->{column} = 0;
8068          !!!cp ('i2');          !!!cp ('i2');
8069        } 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  
8070          !!!cp ('i4');          !!!cp ('i4');
8071          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8072          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8073        }        }
8074      };      };
8075      $p->{prev_char} = [-1, -1, -1];  
8076      $p->{next_char} = -1;      $p->{read_until} = sub {
8077              #my ($scalar, $specials_range, $offset) = @_;
8078          return 0 if defined $p->{next_nc};
8079    
8080          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8081          my $offset = $_[2] || 0;
8082          
8083          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8084            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8085            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8086              substr ($_[0], $offset)
8087                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8088              my $count = $+[0] - $-[0];
8089              if ($count) {
8090                $p->{column} += $count;
8091                $p->{char_buffer_pos} += $count;
8092                $p->{line_prev} = $p->{line};
8093                $p->{column_prev} = $p->{column} - 1;
8094                $p->{nc} = -1;
8095              }
8096              return $count;
8097            } else {
8098              return 0;
8099            }
8100          } else {
8101            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8102            if ($count) {
8103              $p->{column} += $count;
8104              $p->{column_prev} += $count;
8105              $p->{nc} = -1;
8106            }
8107            return $count;
8108          }
8109        }; # $p->{read_until}
8110    
8111      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8112        my (%opt) = @_;        my (%opt) = @_;
8113        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8114          my $column = $opt{column};
8115          if (defined $opt{token} and defined $opt{token}->{line}) {
8116            $line = $opt{token}->{line};
8117            $column = $opt{token}->{column};
8118          }
8119          warn "Parse error ($opt{type}) at line $line column $column\n";
8120      };      };
8121      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8122        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8123      };      };
8124            
8125        my $char_onerror = sub {
8126          my (undef, $type, %opt) = @_;
8127          $ponerror->(layer => 'encode',
8128                      line => $p->{line}, column => $p->{column} + 1,
8129                      %opt, type => $type);
8130        }; # $char_onerror
8131        $input->onerror ($char_onerror);
8132    
8133      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8134      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8135    
# Line 6419  sub set_inner_html ($$$) { Line 8151  sub set_inner_html ($$$) {
8151          unless defined $p->{content_model};          unless defined $p->{content_model};
8152          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8153    
8154      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8155          ## TODO: Foreign element OK?
8156    
8157      ## Step 3      ## Step 3
8158      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6429  sub set_inner_html ($$$) { Line 8162  sub set_inner_html ($$$) {
8162      $doc->append_child ($root);      $doc->append_child ($root);
8163    
8164      ## Step 5 # MUST      ## Step 5 # MUST
8165      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8166    
8167      undef $p->{head_element};      undef $p->{head_element};
8168    
# Line 6475  sub set_inner_html ($$$) { Line 8208  sub set_inner_html ($$$) {
8208      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8209    
8210      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8211    
8212        delete $p->{parse_error}; # delete loop
8213    } else {    } else {
8214      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";
8215    }    }

Legend:
Removed from v.1.100  
changed lines
  Added in v.1.198

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24