/[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.93 by wakaba, Sat Mar 8 03:43:48 2008 UTC revision 1.204 by wakaba, Sun Oct 5 05:59:35 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91      BODY_EL |
92      HTML_EL |
93      TABLE_CELL_EL |
94      TABLE_ROW_EL |
95      TABLE_ROW_GROUP_EL
96    }
97    
98    sub SCOPING_EL () {
99      BUTTON_EL |
100      CAPTION_EL |
101      HTML_EL |
102      TABLE_EL |
103      TABLE_CELL_EL |
104      MISC_SCOPING_EL
105    }
106    
107    sub TABLE_SCOPING_EL () {
108      HTML_EL |
109      TABLE_EL
110    }
111    
112    sub TABLE_ROWS_SCOPING_EL () {
113      HTML_EL |
114      TABLE_ROW_GROUP_EL
115    }
116    
117    sub TABLE_ROW_SCOPING_EL () {
118      HTML_EL |
119      TABLE_ROW_EL
120    }
121    
122    sub SPECIAL_EL () {
123      ADDRESS_EL |
124      BODY_EL |
125      DIV_EL |
126    
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
132      FORM_EL |
133      FRAMESET_EL |
134      HEADING_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240    };
241    
242    my $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $c1_entity_char = {  my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
400    
401    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
402      my $self = shift;      my (%opt) = @_;
403      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
404      ## TODO: if $charset is supported    };
405      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      ## Step 1        };
410      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
411        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444      }      }
445    
446      ## Step 2      ## Step 2
447      if (defined $self->{input_encoding} and      my $byte_buffer = '';
448          $self->{input_encoding} eq $charset) {      for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        return;        last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476      }      }
477    
478      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
479          ':'.$charset, level => 'w');      ## TODO: <meta charset>
480    
481      ## Step 3      ## Step 5
482      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
483    
484      ## Step 4      ## Step 6
485      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599    }; # $self->{change_encoding}    }; # $self->{change_encoding}
600    
601    my @args = @_; shift @args; # $s    my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
620      $s = \ (Encode::decode ($charset, $$bytes_s));      
621      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $return = $self->parse_char_string ($s, @args);  
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_string  } # parse_byte_stream
648    
649  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 655  sub parse_byte_string ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 175  sub parse_string ($$$;$) { Line 676  sub parse_string ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
681    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
682    my $line = 1;    $self->{column_prev} = -1;
683    my $column = 0;    $self->{column} = 0;
684    $self->{set_next_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      $self->{next_char} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
697      $self->{next_char} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698      $column++;        if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709          } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_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);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2990          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2991          ## reconsume          ## reconsume
2992    
2993          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2994    
2995          redo A;          redo A;
2996        } else {        } else {
2997          !!!cp (221);          !!!cp (221);
2998            my $s = '';
2999            $self->{read_until}->($s, q[>], 0);
3000    
3001          ## Stay in the state          ## Stay in the state
3002          !!!next-input-character;          !!!next-input-character;
3003          redo A;          redo A;
3004        }        }
3005      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3006        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3007      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3008    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3009          
3010    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3011  } # _get_next_token          !!!cp (221.1);
3012            $self->{state} = CDATA_SECTION_MSE1_STATE;
3013            !!!next-input-character;
3014            redo A;
3015          } elsif ($self->{nc} == -1) {
3016            $self->{state} = DATA_STATE;
3017            !!!next-input-character;
3018            if (length $self->{ct}->{data}) { # character
3019              !!!cp (221.2);
3020              !!!emit ($self->{ct}); # character
3021            } else {
3022              !!!cp (221.3);
3023              ## No token to emit. $self->{ct} is discarded.
3024            }        
3025            redo A;
3026          } else {
3027            !!!cp (221.4);
3028            $self->{ct}->{data} .= chr $self->{nc};
3029            $self->{read_until}->($self->{ct}->{data},
3030                                  q<]>,
3031                                  length $self->{ct}->{data});
3032    
3033  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3034    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3035            redo A;
3036          }
3037    
3038    if ({        ## ISSUE: "text tokens" in spec.
3039         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3040         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3041         $additional => 1,          !!!cp (221.5);
3042        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3043      !!!cp (1001);          !!!next-input-character;
3044      ## Don't consume          redo A;
3045      ## No error        } else {
3046      return undef;          !!!cp (221.6);
3047    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3048      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3049      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3050          $self->{next_char} == 0x0058) { # X          redo A;
3051        my $code;        }
3052        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3053          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3054          !!!next-input-character;          $self->{state} = DATA_STATE;
3055          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3056              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3057            !!!cp (1002);            !!!cp (221.7);
3058            $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;  
3059          } else {          } else {
3060            !!!cp (1007);            !!!cp (221.8);
3061            !!!parse-error (type => 'no refc');            ## No token to emit. $self->{ct} is discarded.
3062          }          }
3063            redo A;
3064          } elsif ($self->{nc} == 0x005D) { # ]
3065            !!!cp (221.9); # character
3066            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3067            ## Stay in the state.
3068            !!!next-input-character;
3069            redo A;
3070          } else {
3071            !!!cp (221.11);
3072            $self->{ct}->{data} .= ']]'; # character
3073            $self->{state} = CDATA_SECTION_STATE;
3074            ## Reconsume.
3075            redo A;
3076          }
3077        } elsif ($self->{state} == ENTITY_STATE) {
3078          if ($is_space->{$self->{nc}} or
3079              {
3080                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3081                $self->{entity_add} => 1,
3082              }->{$self->{nc}}) {
3083            !!!cp (1001);
3084            ## Don't consume
3085            ## No error
3086            ## Return nothing.
3087            #
3088          } elsif ($self->{nc} == 0x0023) { # #
3089            !!!cp (999);
3090            $self->{state} = ENTITY_HASH_STATE;
3091            $self->{s_kwd} = '#';
3092            !!!next-input-character;
3093            redo A;
3094          } elsif ((0x0041 <= $self->{nc} and
3095                    $self->{nc} <= 0x005A) or # A..Z
3096                   (0x0061 <= $self->{nc} and
3097                    $self->{nc} <= 0x007A)) { # a..z
3098            !!!cp (998);
3099            require Whatpm::_NamedEntityList;
3100            $self->{state} = ENTITY_NAME_STATE;
3101            $self->{s_kwd} = chr $self->{nc};
3102            $self->{entity__value} = $self->{s_kwd};
3103            $self->{entity__match} = 0;
3104            !!!next-input-character;
3105            redo A;
3106          } else {
3107            !!!cp (1027);
3108            !!!parse-error (type => 'bare ero');
3109            ## Return nothing.
3110            #
3111          }
3112    
3113          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3114            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3115            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3116            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3117          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3118            !!!cp (1009);  
3119            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3120            $code = 0xFFFD;          !!!cp (997);
3121          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3122            !!!cp (1010);          ## Reconsume.
3123            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3124            $code = 0x000A;                    line => $self->{line_prev},
3125          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3126            !!!cp (1011);                   });
3127            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3128            $code = $c1_entity_char->{$code};        } else {
3129          }          !!!cp (996);
3130            $self->{ca}->{value} .= '&';
3131          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3132                  has_reference => 1};          ## Reconsume.
3133        } # X          redo A;
3134      } elsif (0x0030 <= $self->{next_char} and        }
3135               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3136        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3137        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3138                  !!!cp (995);
3139        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3140                  $self->{next_char} <= 0x0039) { # 0..9          $self->{s_kwd} .= chr $self->{nc};
3141            !!!next-input-character;
3142            redo A;
3143          } elsif (0x0030 <= $self->{nc} and
3144                   $self->{nc} <= 0x0039) { # 0..9
3145            !!!cp (994);
3146            $self->{state} = NCR_NUM_STATE;
3147            $self->{s_kwd} = $self->{nc} - 0x0030;
3148            !!!next-input-character;
3149            redo A;
3150          } else {
3151            !!!parse-error (type => 'bare nero',
3152                            line => $self->{line_prev},
3153                            column => $self->{column_prev} - 1);
3154    
3155            ## NOTE: According to the spec algorithm, nothing is returned,
3156            ## and then "&#" is appended to the parent element or the attribute
3157            ## value in the later processing.
3158    
3159            if ($self->{prev_state} == DATA_STATE) {
3160              !!!cp (1019);
3161              $self->{state} = $self->{prev_state};
3162              ## Reconsume.
3163              !!!emit ({type => CHARACTER_TOKEN,
3164                        data => '&#',
3165                        line => $self->{line_prev},
3166                        column => $self->{column_prev} - 1,
3167                       });
3168              redo A;
3169            } else {
3170              !!!cp (993);
3171              $self->{ca}->{value} .= '&#';
3172              $self->{state} = $self->{prev_state};
3173              ## Reconsume.
3174              redo A;
3175            }
3176          }
3177        } elsif ($self->{state} == NCR_NUM_STATE) {
3178          if (0x0030 <= $self->{nc} and
3179              $self->{nc} <= 0x0039) { # 0..9
3180          !!!cp (1012);          !!!cp (1012);
3181          $code *= 10;          $self->{s_kwd} *= 10;
3182          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3183                    
3184            ## Stay in the state.
3185          !!!next-input-character;          !!!next-input-character;
3186        }          redo A;
3187          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3188          !!!cp (1013);          !!!cp (1013);
3189          !!!next-input-character;          !!!next-input-character;
3190            #
3191        } else {        } else {
3192          !!!cp (1014);          !!!cp (1014);
3193          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3194            ## Reconsume.
3195            #
3196        }        }
3197    
3198        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3199          my $l = $self->{line_prev};
3200          my $c = $self->{column_prev};
3201          if ($charref_map->{$code}) {
3202          !!!cp (1015);          !!!cp (1015);
3203          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3204          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3205                            line => $l, column => $c);
3206            $code = $charref_map->{$code};
3207        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3208          !!!cp (1016);          !!!cp (1016);
3209          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => 'invalid character reference',
3210                            text => (sprintf 'U-%08X', $code),
3211                            line => $l, column => $c);
3212          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!cp (1017);  
         !!!parse-error (type => 'CR character reference');  
         $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};  
3213        }        }
3214          
3215        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3216      } else {          !!!cp (992);
3217        !!!cp (1019);          $self->{state} = $self->{prev_state};
3218        !!!parse-error (type => 'bare nero');          ## Reconsume.
3219        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3220        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3221        return undef;                   });
3222      }          redo A;
3223    } elsif ((0x0041 <= $self->{next_char} and        } else {
3224              $self->{next_char} <= 0x005A) or          !!!cp (991);
3225             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3226              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3227      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3228      !!!next-input-character;          ## Reconsume.
3229            redo A;
3230      my $value = $entity_name;        }
3231      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3232      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3233      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3234              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3235      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3236             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3237             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3238               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3239              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3240               $self->{next_char} <= 0x007A) or # z          redo A;
3241              (0x0030 <= $self->{next_char} and # 0        } else {
3242               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3243              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3244        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3245        if (defined $EntityChar->{$entity_name}) {  
3246          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3247            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3248            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3249            $match = 1;  
3250            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3251            last;            !!!cp (1005);
3252              $self->{state} = $self->{prev_state};
3253              ## Reconsume.
3254              !!!emit ({type => CHARACTER_TOKEN,
3255                        data => '&' . $self->{s_kwd},
3256                        line => $self->{line_prev},
3257                        column => $self->{column_prev} - length $self->{s_kwd},
3258                       });
3259              redo A;
3260          } else {          } else {
3261            !!!cp (1021);            !!!cp (989);
3262            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3263            $match = -1;            $self->{state} = $self->{prev_state};
3264            !!!next-input-character;            ## Reconsume.
3265              redo A;
3266          }          }
3267        } else {        }
3268          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3269          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3270          $match *= 2;          # 0..9
3271            !!!cp (1002);
3272            $self->{s_kwd} *= 0x10;
3273            $self->{s_kwd} += $self->{nc} - 0x0030;
3274            ## Stay in the state.
3275            !!!next-input-character;
3276            redo A;
3277          } elsif (0x0061 <= $self->{nc} and
3278                   $self->{nc} <= 0x0066) { # a..f
3279            !!!cp (1003);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0041 <= $self->{nc} and
3286                   $self->{nc} <= 0x0046) { # A..F
3287            !!!cp (1004);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif ($self->{nc} == 0x003B) { # ;
3294            !!!cp (1006);
3295          !!!next-input-character;          !!!next-input-character;
3296            #
3297          } else {
3298            !!!cp (1007);
3299            !!!parse-error (type => 'no refc',
3300                            line => $self->{line},
3301                            column => $self->{column});
3302            ## Reconsume.
3303            #
3304        }        }
3305      }  
3306              my $code = $self->{s_kwd};
3307      if ($match > 0) {        my $l = $self->{line_prev};
3308        !!!cp (1023);        my $c = $self->{column_prev};
3309        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3310      } elsif ($match < 0) {          !!!cp (1008);
3311        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3312        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3313          !!!cp (1024);                          line => $l, column => $c);
3314          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $code = $charref_map->{$code};
3315          } elsif ($code > 0x10FFFF) {
3316            !!!cp (1009);
3317            !!!parse-error (type => 'invalid character reference',
3318                            text => (sprintf 'U-%08X', $code),
3319                            line => $l, column => $c);
3320            $code = 0xFFFD;
3321          }
3322    
3323          if ($self->{prev_state} == DATA_STATE) {
3324            !!!cp (988);
3325            $self->{state} = $self->{prev_state};
3326            ## Reconsume.
3327            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3328                      line => $l, column => $c,
3329                     });
3330            redo A;
3331          } else {
3332            !!!cp (987);
3333            $self->{ca}->{value} .= chr $code;
3334            $self->{ca}->{has_reference} = 1;
3335            $self->{state} = $self->{prev_state};
3336            ## Reconsume.
3337            redo A;
3338          }
3339        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3340          if (length $self->{s_kwd} < 30 and
3341              ## NOTE: Some number greater than the maximum length of entity name
3342              ((0x0041 <= $self->{nc} and # a
3343                $self->{nc} <= 0x005A) or # x
3344               (0x0061 <= $self->{nc} and # a
3345                $self->{nc} <= 0x007A) or # z
3346               (0x0030 <= $self->{nc} and # 0
3347                $self->{nc} <= 0x0039) or # 9
3348               $self->{nc} == 0x003B)) { # ;
3349            our $EntityChar;
3350            $self->{s_kwd} .= chr $self->{nc};
3351            if (defined $EntityChar->{$self->{s_kwd}}) {
3352              if ($self->{nc} == 0x003B) { # ;
3353                !!!cp (1020);
3354                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3355                $self->{entity__match} = 1;
3356                !!!next-input-character;
3357                #
3358              } else {
3359                !!!cp (1021);
3360                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3361                $self->{entity__match} = -1;
3362                ## Stay in the state.
3363                !!!next-input-character;
3364                redo A;
3365              }
3366            } else {
3367              !!!cp (1022);
3368              $self->{entity__value} .= chr $self->{nc};
3369              $self->{entity__match} *= 2;
3370              ## Stay in the state.
3371              !!!next-input-character;
3372              redo A;
3373            }
3374          }
3375    
3376          my $data;
3377          my $has_ref;
3378          if ($self->{entity__match} > 0) {
3379            !!!cp (1023);
3380            $data = $self->{entity__value};
3381            $has_ref = 1;
3382            #
3383          } elsif ($self->{entity__match} < 0) {
3384            !!!parse-error (type => 'no refc');
3385            if ($self->{prev_state} != DATA_STATE and # in attribute
3386                $self->{entity__match} < -1) {
3387              !!!cp (1024);
3388              $data = '&' . $self->{s_kwd};
3389              #
3390            } else {
3391              !!!cp (1025);
3392              $data = $self->{entity__value};
3393              $has_ref = 1;
3394              #
3395            }
3396        } else {        } else {
3397          !!!cp (1025);          !!!cp (1026);
3398          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!parse-error (type => 'bare ero',
3399                            line => $self->{line_prev},
3400                            column => $self->{column_prev} - length $self->{s_kwd});
3401            $data = '&' . $self->{s_kwd};
3402            #
3403          }
3404      
3405          ## NOTE: In these cases, when a character reference is found,
3406          ## it is consumed and a character token is returned, or, otherwise,
3407          ## nothing is consumed and returned, according to the spec algorithm.
3408          ## In this implementation, anything that has been examined by the
3409          ## tokenizer is appended to the parent element or the attribute value
3410          ## as string, either literal string when no character reference or
3411          ## entity-replaced string otherwise, in this stage, since any characters
3412          ## that would not be consumed are appended in the data state or in an
3413          ## appropriate attribute value state anyway.
3414    
3415          if ($self->{prev_state} == DATA_STATE) {
3416            !!!cp (986);
3417            $self->{state} = $self->{prev_state};
3418            ## Reconsume.
3419            !!!emit ({type => CHARACTER_TOKEN,
3420                      data => $data,
3421                      line => $self->{line_prev},
3422                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3423                     });
3424            redo A;
3425          } else {
3426            !!!cp (985);
3427            $self->{ca}->{value} .= $data;
3428            $self->{ca}->{has_reference} = 1 if $has_ref;
3429            $self->{state} = $self->{prev_state};
3430            ## Reconsume.
3431            redo A;
3432        }        }
3433      } else {      } else {
3434        !!!cp (1026);        die "$0: $self->{state}: Unknown state";
       !!!parse-error (type => 'bare ero');  
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value};  
3435      }      }
3436    } else {    } # A  
3437      !!!cp (1027);  
3438      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3439      !!!parse-error (type => 'bare ero');  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3440    
3441  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3442    my $self = shift;    my $self = shift;
# Line 2400  sub _initialize_tree_constructor ($) { Line 3445  sub _initialize_tree_constructor ($) {
3445    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3446    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3447    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3448      $self->{document}->set_user_data (manakai_source_line => 1);
3449      $self->{document}->set_user_data (manakai_source_column => 1);
3450  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3451    
3452  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2419  sub _construct_tree ($) { Line 3466  sub _construct_tree ($) {
3466    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3467    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3468    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3469        
3470    !!!next-token;    !!!next-token;
3471    
3472    undef $self->{form_element};    undef $self->{form_element};
3473    undef $self->{head_element};    undef $self->{head_element};
3474      undef $self->{head_element_inserted};
3475    $self->{open_elements} = [];    $self->{open_elements} = [];
3476    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3477    
# Line 2454  sub _tree_construction_initial ($) { Line 3498  sub _tree_construction_initial ($) {
3498        ## language.        ## language.
3499        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3500        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3501        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3502        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3503            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3504          !!!cp ('t1');          !!!cp ('t1');
3505          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3506        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3507          !!!cp ('t2');          !!!cp ('t2');
3508          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3509          !!!parse-error (type => 'not HTML5');        } elsif (defined $token->{pubid}) {
3510            if ($token->{pubid} eq 'XSLT-compat') {
3511              !!!cp ('t1.2');
3512              !!!parse-error (type => 'XSLT-compat', token => $token,
3513                              level => $self->{level}->{should});
3514            } else {
3515              !!!parse-error (type => 'not HTML5', token => $token);
3516            }
3517        } else {        } else {
3518          !!!cp ('t3');          !!!cp ('t3');
3519            #
3520        }        }
3521                
3522        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3523          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3524        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3525            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3526        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3527            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3528        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3529        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3530        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2481  sub _tree_construction_initial ($) { Line 3532  sub _tree_construction_initial ($) {
3532        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3533          !!!cp ('t4');          !!!cp ('t4');
3534          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3535        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3536          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3537          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3538          if ({          my $prefix = [
3539            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3540            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3541            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3542            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3543            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3544            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3545            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3546            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3547            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3548            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3549            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3550            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3551            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3552            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3553            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3554            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3555            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3556            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3557            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3562            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3563            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3564            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3565            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3566            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3567            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3568            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3569            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3570            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3571            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3572            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3573            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3574            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3575            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3576            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3577            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3578            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3579            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3580            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3583            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3584            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3585            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3586            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3587            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3588            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3589            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3590            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3591            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3592            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3593            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3594            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3595            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3596            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3597            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3598            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3599            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3600            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3601            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3602            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3603            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3604            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3605            "-//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}) {  
3606            !!!cp ('t5');            !!!cp ('t5');
3607            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3608          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3609                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3610            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3611              !!!cp ('t6');              !!!cp ('t6');
3612              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3613            } else {            } else {
3614              !!!cp ('t7');              !!!cp ('t7');
3615              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3616            }            }
3617          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3618                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3619            !!!cp ('t8');            !!!cp ('t8');
3620            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3621          } else {          } else {
# Line 2579  sub _tree_construction_initial ($) { Line 3624  sub _tree_construction_initial ($) {
3624        } else {        } else {
3625          !!!cp ('t10');          !!!cp ('t10');
3626        }        }
3627        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3628          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3629          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3630          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") {
3631            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3632              ## marked as quirks.
3633            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3634            !!!cp ('t11');            !!!cp ('t11');
3635          } else {          } else {
# Line 2602  sub _tree_construction_initial ($) { Line 3648  sub _tree_construction_initial ($) {
3648                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3649               }->{$token->{type}}) {               }->{$token->{type}}) {
3650        !!!cp ('t14');        !!!cp ('t14');
3651        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3652        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3653        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3654        ## reprocess        ## reprocess
3655          !!!ack-later;
3656        return;        return;
3657      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3658        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3659          ## Ignore the token          ## Ignore the token
3660    
3661          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2623  sub _tree_construction_initial ($) { Line 3670  sub _tree_construction_initial ($) {
3670          !!!cp ('t17');          !!!cp ('t17');
3671        }        }
3672    
3673        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3674        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3675        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3676        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 3699  sub _tree_construction_root_element ($)
3699    B: {    B: {
3700        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3701          !!!cp ('t19');          !!!cp ('t19');
3702          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3703          ## Ignore the token          ## Ignore the token
3704          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3705          !!!next-token;          !!!next-token;
# Line 2665  sub _tree_construction_root_element ($) Line 3712  sub _tree_construction_root_element ($)
3712          !!!next-token;          !!!next-token;
3713          redo B;          redo B;
3714        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3715          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3716            ## Ignore the token.            ## Ignore the token.
3717    
3718            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2686  sub _tree_construction_root_element ($) Line 3733  sub _tree_construction_root_element ($)
3733        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3734          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3735            my $root_element;            my $root_element;
3736            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3737            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3738            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3739                  [$root_element, $el_category->{html}];
3740    
3741            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3742              !!!cp ('t24');              !!!cp ('t24');
3743              $self->{application_cache_selection}              $self->{application_cache_selection}
3744                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3745              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3746                ## According to Hixie (#whatwg 2008-03-19), it should be
3747                ## resolved against the base URI of the document in HTML
3748                ## or xml:base of the element in XHTML.
3749            } else {            } else {
3750              !!!cp ('t25');              !!!cp ('t25');
3751              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3752            }            }
3753    
3754              !!!nack ('t25c');
3755    
3756            !!!next-token;            !!!next-token;
3757            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3758          } else {          } else {
# Line 2716  sub _tree_construction_root_element ($) Line 3769  sub _tree_construction_root_element ($)
3769          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3770        }        }
3771    
3772      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3773        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3774      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3775      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3776    
3777      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3778    
3779      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3780        !!!ack-later;
3781      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3782    } # B    } # B
3783    
3784    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2743  sub _reset_insertion_mode ($) { Line 3796  sub _reset_insertion_mode ($) {
3796            
3797      ## Step 3      ## Step 3
3798      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3799        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3800          $last = 1;          $last = 1;
3801          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3802            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3803                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3804              !!!cp ('t27');          } else {
3805              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3806          }          }
3807        }        }
3808              
3809        ## Step 4..13        ## Step 4..14
3810        my $new_mode = {        my $new_mode;
3811          if ($node->[1] & FOREIGN_EL) {
3812            !!!cp ('t28.1');
3813            ## NOTE: Strictly spaking, the line below only applies to MathML and
3814            ## SVG elements.  Currently the HTML syntax supports only MathML and
3815            ## SVG elements as foreigners.
3816            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3817          } elsif ($node->[1] & TABLE_CELL_EL) {
3818            if ($last) {
3819              !!!cp ('t28.2');
3820              #
3821            } else {
3822              !!!cp ('t28.3');
3823              $new_mode = IN_CELL_IM;
3824            }
3825          } else {
3826            !!!cp ('t28.4');
3827            $new_mode = {
3828                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3829                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3830                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3831                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3832                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3833                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2779  sub _reset_insertion_mode ($) { Line 3838  sub _reset_insertion_mode ($) {
3838                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3839                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3840                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3841                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3842          }
3843        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3844                
3845        ## Step 14        ## Step 15
3846        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3847          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3848            !!!cp ('t29');            !!!cp ('t29');
3849            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2797  sub _reset_insertion_mode ($) { Line 3857  sub _reset_insertion_mode ($) {
3857          !!!cp ('t31');          !!!cp ('t31');
3858        }        }
3859                
3860        ## Step 15        ## Step 16
3861        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3862                
3863        ## Step 16        ## Step 17
3864        $i--;        $i--;
3865        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3866                
3867        ## Step 17        ## Step 18
3868        redo S3;        redo S3;
3869      } # S3      } # S3
3870    
# Line 2908  sub _tree_construction_main ($) { Line 3968  sub _tree_construction_main ($) {
3968      !!!cp ('t39');      !!!cp ('t39');
3969    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3970    
3971    my $parse_rcdata = sub ($$) {    my $insert;
3972      my ($content_model_flag, $insert) = @_;  
3973      my $parse_rcdata = sub ($) {
3974        my ($content_model_flag) = @_;
3975    
3976      ## Step 1      ## Step 1
3977      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3978      my $el;      my $el;
3979      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3980    
3981      ## Step 2      ## Step 2
3982      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3983    
3984      ## Step 3      ## Step 3
3985      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2925  sub _tree_construction_main ($) { Line 3987  sub _tree_construction_main ($) {
3987    
3988      ## Step 4      ## Step 4
3989      my $text = '';      my $text = '';
3990        !!!nack ('t40.1');
3991      !!!next-token;      !!!next-token;
3992      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3993        !!!cp ('t40');        !!!cp ('t40');
# Line 2947  sub _tree_construction_main ($) { Line 4010  sub _tree_construction_main ($) {
4010          $token->{tag_name} eq $start_tag_name) {          $token->{tag_name} eq $start_tag_name) {
4011        !!!cp ('t42');        !!!cp ('t42');
4012        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!cp ('t43');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!cp ('t44');  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4013      } else {      } else {
4014        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
4015          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4016            !!!cp ('t43');
4017            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4018          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4019            !!!cp ('t44');
4020            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4021          } else {
4022            die "$0: $content_model_flag in parse_rcdata";
4023          }
4024      }      }
4025      !!!next-token;      !!!next-token;
4026    }; # $parse_rcdata    }; # $parse_rcdata
4027    
4028    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4029      my $script_el;      my $script_el;
4030      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4031      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4032    
4033      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4034      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4035            
4036      my $text = '';      my $text = '';
4037        !!!nack ('t45.1');
4038      !!!next-token;      !!!next-token;
4039      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4040        !!!cp ('t45');        !!!cp ('t45');
# Line 2988  sub _tree_construction_main ($) { Line 4054  sub _tree_construction_main ($) {
4054        ## Ignore the token        ## Ignore the token
4055      } else {      } else {
4056        !!!cp ('t48');        !!!cp ('t48');
4057        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!parse-error (type => 'in CDATA:#eof', token => $token);
4058        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4059        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4060      }      }
# Line 3011  sub _tree_construction_main ($) { Line 4077  sub _tree_construction_main ($) {
4077      !!!next-token;      !!!next-token;
4078    }; # $script_start_tag    }; # $script_start_tag
4079    
4080      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4081      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4082      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4083      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4084    
4085    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4086      my $tag_name = shift;      my $end_tag_token = shift;
4087        my $tag_name = $end_tag_token->{tag_name};
4088    
4089        ## NOTE: The adoption agency algorithm (AAA).
4090    
4091      FET: {      FET: {
4092        ## Step 1        ## Step 1
4093        my $formatting_element;        my $formatting_element;
4094        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4095        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4096          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4097              !!!cp ('t52');
4098              last AFE;
4099            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4100                         eq $tag_name) {
4101            !!!cp ('t51');            !!!cp ('t51');
4102            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4103            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4104            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4105          }          }
4106        } # AFE        } # AFE
4107        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4108          !!!cp ('t53');          !!!cp ('t53');
4109          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4110          ## Ignore the token          ## Ignore the token
4111          !!!next-token;          !!!next-token;
4112          return;          return;
# Line 3048  sub _tree_construction_main ($) { Line 4123  sub _tree_construction_main ($) {
4123              last INSCOPE;              last INSCOPE;
4124            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4125              !!!cp ('t55');              !!!cp ('t55');
4126              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
4127                                text => $token->{tag_name},
4128                                token => $end_tag_token);
4129              ## Ignore the token              ## Ignore the token
4130              !!!next-token;              !!!next-token;
4131              return;              return;
4132            }            }
4133          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4134            !!!cp ('t56');            !!!cp ('t56');
4135            $in_scope = 0;            $in_scope = 0;
4136          }          }
4137        } # INSCOPE        } # INSCOPE
4138        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4139          !!!cp ('t57');          !!!cp ('t57');
4140          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag',
4141                            text => $token->{tag_name},
4142                            token => $end_tag_token);
4143          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4144          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4145          return;          return;
4146        }        }
4147        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4148          !!!cp ('t58');          !!!cp ('t58');
4149          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!parse-error (type => 'not closed',
4150                            text => $self->{open_elements}->[-1]->[0]
4151                                ->manakai_local_name,
4152                            token => $end_tag_token);
4153        }        }
4154                
4155        ## Step 2        ## Step 2
# Line 3078  sub _tree_construction_main ($) { Line 4157  sub _tree_construction_main ($) {
4157        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4158        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4159          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4160          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4161              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4162              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4163               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4164            !!!cp ('t59');            !!!cp ('t59');
4165            $furthest_block = $node;            $furthest_block = $node;
4166            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4167              ## NOTE: The topmost (eldest) node.
4168          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4169            !!!cp ('t60');            !!!cp ('t60');
4170            last OE;            last OE;
# Line 3167  sub _tree_construction_main ($) { Line 4247  sub _tree_construction_main ($) {
4247        } # S7          } # S7  
4248                
4249        ## Step 8        ## Step 8
4250        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4251            my $foster_parent_element;
4252            my $next_sibling;
4253            OE: for (reverse 0..$#{$self->{open_elements}}) {
4254              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4255                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4256                                 if (defined $parent and $parent->node_type == 1) {
4257                                   !!!cp ('t65.1');
4258                                   $foster_parent_element = $parent;
4259                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4260                                 } else {
4261                                   !!!cp ('t65.2');
4262                                   $foster_parent_element
4263                                     = $self->{open_elements}->[$_ - 1]->[0];
4264                                 }
4265                                 last OE;
4266                               }
4267                             } # OE
4268                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4269                               unless defined $foster_parent_element;
4270            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4271            $open_tables->[-1]->[1] = 1; # tainted
4272          } else {
4273            !!!cp ('t65.3');
4274            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4275          }
4276                
4277        ## Step 9        ## Step 9
4278        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3206  sub _tree_construction_main ($) { Line 4311  sub _tree_construction_main ($) {
4311            $i = $_;            $i = $_;
4312          }          }
4313        } # OE        } # OE
4314        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4315                
4316        ## Step 14        ## Step 14
4317        redo FET;        redo FET;
4318      } # FET      } # FET
4319    }; # $formatting_end_tag    }; # $formatting_end_tag
4320    
4321    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4322      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4323    }; # $insert_to_current    }; # $insert_to_current
4324    
4325    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4326                         my $child = shift;      my $child = shift;
4327                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4328                              table => 1, tbody => 1, tfoot => 1,        # MUST
4329                              thead => 1, tr => 1,        my $foster_parent_element;
4330                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4331                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4332                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
4333                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4334                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4335                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3245  sub _tree_construction_main ($) { Line 4347  sub _tree_construction_main ($) {
4347                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4348                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4349                             ($child, $next_sibling);                             ($child, $next_sibling);
4350                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4351                           !!!cp ('t72');      } else {
4352                           $self->{open_elements}->[-1]->[0]->append_child ($child);        !!!cp ('t72');
4353                         }        $self->{open_elements}->[-1]->[0]->append_child ($child);
4354        }
4355    }; # $insert_to_foster    }; # $insert_to_foster
4356    
4357    my $insert;    ## NOTE: Insert a character (MUST): When a character is inserted, if
4358      ## the last node that was inserted by the parser is a Text node and
4359      ## the character has to be inserted after that node, then the
4360      ## character is appended to the Text node.  However, if any other
4361      ## node is inserted by the parser, then a new Text node is created
4362      ## and the character is appended as that Text node.  If I'm not
4363      ## wrong, for a parser with scripting disabled, there are only two
4364      ## cases where this occurs.  One is the case where an element node
4365      ## is inserted to the |head| element.  This is covered by using the
4366      ## |$self->{head_element_inserted}| flag.  Another is the case where
4367      ## an element or comment is inserted into the |table| subtree while
4368      ## foster parenting happens.  This is covered by using the [2] flag
4369      ## of the |$open_tables| structure.  All other cases are handled
4370      ## simply by calling |manakai_append_text| method.
4371    
4372      ## TODO: |<body><script>document.write("a<br>");
4373      ## document.body.removeChild (document.body.lastChild);
4374      ## document.write ("b")</script>|
4375    
4376    B: {    B: while (1) {
4377      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4378        !!!cp ('t73');        !!!cp ('t73');
4379        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4380        ## Ignore the token        ## Ignore the token
4381        ## Stay in the phase        ## Stay in the phase
4382        !!!next-token;        !!!next-token;
4383        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;  
4384      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4385               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4386        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4387          !!!cp ('t79');          !!!cp ('t79');
4388          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4389          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4390        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4391          !!!cp ('t80');          !!!cp ('t80');
4392          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4393          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4394        } else {        } else {
4395          !!!cp ('t81');          !!!cp ('t81');
4396        }        }
4397    
4398        !!!cp ('t82');        !!!cp ('t82');
4399        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
4400        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4401        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4402          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3318  sub _tree_construction_main ($) { Line 4406  sub _tree_construction_main ($) {
4406               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4407          }          }
4408        }        }
4409          !!!nack ('t84.1');
4410        !!!next-token;        !!!next-token;
4411        redo B;        next B;
4412      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4413        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4414        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3331  sub _tree_construction_main ($) { Line 4420  sub _tree_construction_main ($) {
4420        } else {        } else {
4421          !!!cp ('t87');          !!!cp ('t87');
4422          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4423            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4424        }        }
4425        !!!next-token;        !!!next-token;
4426        redo B;        next B;
4427      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4428        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4429          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4430            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4431            !!!next-token;
4432            next B;
4433          } elsif ($token->{type} == START_TAG_TOKEN) {
4434            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4435                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4436                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4437                ($token->{tag_name} eq 'svg' and
4438                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4439              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4440              !!!cp ('t87.2');
4441              #
4442            } elsif ({
4443                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4444                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4445                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4446                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4447                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4448                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4449                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4450                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4451                     }->{$token->{tag_name}}) {
4452              !!!cp ('t87.2');
4453              !!!parse-error (type => 'not closed',
4454                              text => $self->{open_elements}->[-1]->[0]
4455                                  ->manakai_local_name,
4456                              token => $token);
4457    
4458              pop @{$self->{open_elements}}
4459                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4460    
4461              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4462              ## Reprocess.
4463              next B;
4464            } else {
4465              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4466              my $tag_name = $token->{tag_name};
4467              if ($nsuri eq $SVG_NS) {
4468                $tag_name = {
4469                   altglyph => 'altGlyph',
4470                   altglyphdef => 'altGlyphDef',
4471                   altglyphitem => 'altGlyphItem',
4472                   animatecolor => 'animateColor',
4473                   animatemotion => 'animateMotion',
4474                   animatetransform => 'animateTransform',
4475                   clippath => 'clipPath',
4476                   feblend => 'feBlend',
4477                   fecolormatrix => 'feColorMatrix',
4478                   fecomponenttransfer => 'feComponentTransfer',
4479                   fecomposite => 'feComposite',
4480                   feconvolvematrix => 'feConvolveMatrix',
4481                   fediffuselighting => 'feDiffuseLighting',
4482                   fedisplacementmap => 'feDisplacementMap',
4483                   fedistantlight => 'feDistantLight',
4484                   feflood => 'feFlood',
4485                   fefunca => 'feFuncA',
4486                   fefuncb => 'feFuncB',
4487                   fefuncg => 'feFuncG',
4488                   fefuncr => 'feFuncR',
4489                   fegaussianblur => 'feGaussianBlur',
4490                   feimage => 'feImage',
4491                   femerge => 'feMerge',
4492                   femergenode => 'feMergeNode',
4493                   femorphology => 'feMorphology',
4494                   feoffset => 'feOffset',
4495                   fepointlight => 'fePointLight',
4496                   fespecularlighting => 'feSpecularLighting',
4497                   fespotlight => 'feSpotLight',
4498                   fetile => 'feTile',
4499                   feturbulence => 'feTurbulence',
4500                   foreignobject => 'foreignObject',
4501                   glyphref => 'glyphRef',
4502                   lineargradient => 'linearGradient',
4503                   radialgradient => 'radialGradient',
4504                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4505                   textpath => 'textPath',  
4506                }->{$tag_name} || $tag_name;
4507              }
4508    
4509              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4510    
4511              ## "adjust foreign attributes" - done in insert-element-f
4512    
4513              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4514    
4515              if ($self->{self_closing}) {
4516                pop @{$self->{open_elements}};
4517                !!!ack ('t87.3');
4518              } else {
4519                !!!cp ('t87.4');
4520              }
4521    
4522              !!!next-token;
4523              next B;
4524            }
4525          } elsif ($token->{type} == END_TAG_TOKEN) {
4526            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4527            !!!cp ('t87.5');
4528            #
4529          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4530            !!!cp ('t87.6');
4531            !!!parse-error (type => 'not closed',
4532                            text => $self->{open_elements}->[-1]->[0]
4533                                ->manakai_local_name,
4534                            token => $token);
4535    
4536            pop @{$self->{open_elements}}
4537                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4538    
4539            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4540    
4541            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4542            ## Reprocess.
4543            next B;
4544          } else {
4545            die "$0: $token->{type}: Unknown token type";        
4546          }
4547        }
4548    
4549        if ($self->{insertion_mode} & HEAD_IMS) {
4550          if ($token->{type} == CHARACTER_TOKEN) {
4551            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4552              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4553                if ($self->{head_element_inserted}) {
4554                  !!!cp ('t88.3');
4555                  $self->{open_elements}->[-1]->[0]->append_child
4556                    ($self->{document}->create_text_node ($1));
4557                  delete $self->{head_element_inserted};
4558                  ## NOTE: |</head> <link> |
4559                  #
4560                } else {
4561                  !!!cp ('t88.2');
4562                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4563                  ## NOTE: |</head> &#x20;|
4564                  #
4565                }
4566              } else {
4567                !!!cp ('t88.1');
4568                ## Ignore the token.
4569                #
4570              }
4571            unless (length $token->{data}) {            unless (length $token->{data}) {
4572              !!!cp ('t88');              !!!cp ('t88');
4573              !!!next-token;              !!!next-token;
4574              redo B;              next B;
4575            }            }
4576    ## TODO: set $token->{column} appropriately
4577          }          }
4578    
4579          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4580            !!!cp ('t89');            !!!cp ('t89');
4581            ## As if <head>            ## As if <head>
4582            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4583            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4584            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4585                  [$self->{head_element}, $el_category->{head}];
4586    
4587            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4588            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3360  sub _tree_construction_main ($) { Line 4592  sub _tree_construction_main ($) {
4592            !!!cp ('t90');            !!!cp ('t90');
4593            ## As if </noscript>            ## As if </noscript>
4594            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4595            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4596                        
4597            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4598            ## As if </head>            ## As if </head>
# Line 3376  sub _tree_construction_main ($) { Line 4608  sub _tree_construction_main ($) {
4608            !!!cp ('t92');            !!!cp ('t92');
4609          }          }
4610    
4611              ## "after head" insertion mode          ## "after head" insertion mode
4612              ## As if <body>          ## As if <body>
4613              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4614              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4615              ## reprocess          ## reprocess
4616              redo B;          next B;
4617            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4618              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4619                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620                  !!!cp ('t93');              !!!cp ('t93');
4621                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4622                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4623                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4624                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4625                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4626                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4627                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4628                  !!!cp ('t94');              !!!next-token;
4629                  #              next B;
4630                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4631                  !!!cp ('t95');              !!!cp ('t93.2');
4632                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4633                  ## Ignore the token                              token => $token);
4634                  !!!next-token;              ## Ignore the token
4635                  redo B;              !!!nack ('t93.3');
4636                }              !!!next-token;
4637              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4638                !!!cp ('t96');            } else {
4639                ## As if <head>              !!!cp ('t95');
4640                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4641                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4642                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4643                !!!nack ('t95.1');
4644                !!!next-token;
4645                next B;
4646              }
4647            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4648              !!!cp ('t96');
4649              ## As if <head>
4650              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4651              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4652              push @{$self->{open_elements}},
4653                  [$self->{head_element}, $el_category->{head}];
4654    
4655                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4656                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4657              } else {          } else {
4658                !!!cp ('t97');            !!!cp ('t97');
4659              }          }
4660    
4661              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4662                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4663                  !!!cp ('t98');              !!!cp ('t98');
4664                  ## As if </noscript>              ## As if </noscript>
4665                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4666                  !!!parse-error (type => 'in noscript:base');              !!!parse-error (type => 'in noscript', text => 'base',
4667                                              token => $token);
4668                  $self->{insertion_mode} = IN_HEAD_IM;            
4669                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4670                } else {              ## Reprocess in the "in head" insertion mode...
4671                  !!!cp ('t99');            } else {
4672                }              !!!cp ('t99');
4673              }
4674    
4675                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4676                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4677                  !!!cp ('t100');              !!!cp ('t100');
4678                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4679                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4680                } else {              push @{$self->{open_elements}},
4681                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4682                }              $self->{head_element_inserted} = 1;
4683                !!!insert-element ($token->{tag_name}, $token->{attributes});            } else {
4684                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4685                pop @{$self->{open_elements}}            }
4686                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4687                !!!next-token;            pop @{$self->{open_elements}};
4688                redo B;            pop @{$self->{open_elements}} # <head>
4689              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4690                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4691                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4692                  !!!cp ('t102');            next B;
4693                  !!!parse-error (type => 'after head:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'link') {
4694                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4695                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4696                  !!!cp ('t103');              !!!cp ('t102');
4697                }              !!!parse-error (type => 'after head',
4698                !!!insert-element ($token->{tag_name}, $token->{attributes});                              text => $token->{tag_name}, token => $token);
4699                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4700                pop @{$self->{open_elements}}                  [$self->{head_element}, $el_category->{head}];
4701                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4702                !!!next-token;            } else {
4703                redo B;              !!!cp ('t103');
4704              } elsif ($token->{tag_name} eq 'meta') {            }
4705                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4706                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4707                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4708                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4709                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4710                } else {            !!!next-token;
4711                  !!!cp ('t105');            next B;
4712                }          } elsif ($token->{tag_name} eq 'command' or
4713                !!!insert-element ($token->{tag_name}, $token->{attributes});                   $token->{tag_name} eq 'eventsource') {
4714                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4715                ## NOTE: If the insertion mode at the time of the emission
4716                ## of the token was "before head", $self->{insertion_mode}
4717                ## is already changed to |IN_HEAD_IM|.
4718    
4719                ## NOTE: There is a "as if in head" code clone.
4720                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4721                pop @{$self->{open_elements}};
4722                pop @{$self->{open_elements}} # <head>
4723                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4724                !!!ack ('t103.2');
4725                !!!next-token;
4726                next B;
4727              } else {
4728                ## NOTE: "in head noscript" or "after head" insertion mode
4729                ## - in these cases, these tags are treated as same as
4730                ## normal in-body tags.
4731                !!!cp ('t103.3');
4732                #
4733              }
4734            } elsif ($token->{tag_name} eq 'meta') {
4735              ## NOTE: There is a "as if in head" code clone.
4736              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4737                !!!cp ('t104');
4738                !!!parse-error (type => 'after head',
4739                                text => $token->{tag_name}, token => $token);
4740                push @{$self->{open_elements}},
4741                    [$self->{head_element}, $el_category->{head}];
4742                $self->{head_element_inserted} = 1;
4743              } else {
4744                !!!cp ('t105');
4745              }
4746              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4747              my $meta_el = pop @{$self->{open_elements}};
4748    
4749                unless ($self->{confident}) {                unless ($self->{confident}) {
4750                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4751                    !!!cp ('t106');                    !!!cp ('t106');
4752                      ## NOTE: Whether the encoding is supported or not is handled
4753                      ## in the {change_encoding} callback.
4754                    $self->{change_encoding}                    $self->{change_encoding}
4755                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4756                             $token);
4757                                        
4758                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4759                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4760                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4761                                                 ->{has_reference});                                                 ->{has_reference});
4762                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4763                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4764                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4765                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4766                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4767                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4768                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4769                      !!!cp ('t107');                      !!!cp ('t107');
4770                        ## NOTE: Whether the encoding is supported or not is handled
4771                        ## in the {change_encoding} callback.
4772                      $self->{change_encoding}                      $self->{change_encoding}
4773                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4774                               $token);
4775                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4776                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4777                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3514  sub _tree_construction_main ($) { Line 4797  sub _tree_construction_main ($) {
4797                  }                  }
4798                }                }
4799    
4800                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4801                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4802                  !!!ack ('t110.1');
4803                !!!next-token;                !!!next-token;
4804                redo B;                next B;
4805              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4806                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4807                  !!!cp ('t111');              !!!cp ('t111');
4808                  ## As if </noscript>              ## As if </noscript>
4809                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4810                  !!!parse-error (type => 'in noscript:title');              !!!parse-error (type => 'in noscript', text => 'title',
4811                                              token => $token);
4812                  $self->{insertion_mode} = IN_HEAD_IM;            
4813                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4814                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4815                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4816                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t112');
4817                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4818                } else {                              text => $token->{tag_name}, token => $token);
4819                  !!!cp ('t113');              push @{$self->{open_elements}},
4820                }                  [$self->{head_element}, $el_category->{head}];
4821                $self->{head_element_inserted} = 1;
4822                ## NOTE: There is a "as if in head" code clone.            } else {
4823                my $parent = defined $self->{head_element} ? $self->{head_element}              !!!cp ('t113');
4824                    : $self->{open_elements}->[-1]->[0];            }
4825                $parse_rcdata->(RCDATA_CONTENT_MODEL,  
4826                                sub { $parent->append_child ($_[0]) });            ## NOTE: There is a "as if in head" code clone.
4827                pop @{$self->{open_elements}}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4828                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
4829                redo B;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4830              } elsif ($token->{tag_name} eq 'style') {            next B;
4831                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and          } elsif ($token->{tag_name} eq 'style' or
4832                ## insertion mode IN_HEAD_IM)                   $token->{tag_name} eq 'noframes') {
4833                ## NOTE: There is a "as if in head" code clone.            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4834                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## insertion mode IN_HEAD_IM)
4835                  !!!cp ('t114');            ## NOTE: There is a "as if in head" code clone.
4836                  !!!parse-error (type => 'after head:'.$token->{tag_name});            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!cp ('t114');
4838                } else {              !!!parse-error (type => 'after head',
4839                  !!!cp ('t115');                              text => $token->{tag_name}, token => $token);
4840                }              push @{$self->{open_elements}},
4841                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                  [$self->{head_element}, $el_category->{head}];
4842                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4843                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4844                redo B;              !!!cp ('t115');
4845              }
4846              $parse_rcdata->(CDATA_CONTENT_MODEL);
4847              pop @{$self->{open_elements}} # <head>
4848                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4849              next B;
4850              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4851                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4852                  !!!cp ('t116');                  !!!cp ('t116');
4853                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4854                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4855                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4856                    !!!nack ('t116.1');
4857                  !!!next-token;                  !!!next-token;
4858                  redo B;                  next B;
4859                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4860                  !!!cp ('t117');                  !!!cp ('t117');
4861                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript', text => 'noscript',
4862                                    token => $token);
4863                  ## Ignore the token                  ## Ignore the token
4864                    !!!nack ('t117.1');
4865                  !!!next-token;                  !!!next-token;
4866                  redo B;                  next B;
4867                } else {                } else {
4868                  !!!cp ('t118');                  !!!cp ('t118');
4869                  #                  #
4870                }                }
4871              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4872                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4873                  !!!cp ('t119');              !!!cp ('t119');
4874                  ## As if </noscript>              ## As if </noscript>
4875                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4876                  !!!parse-error (type => 'in noscript:script');              !!!parse-error (type => 'in noscript', text => 'script',
4877                                              token => $token);
4878                  $self->{insertion_mode} = IN_HEAD_IM;            
4879                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4880                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4881                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4882                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t120');
4883                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4884                } else {                              text => $token->{tag_name}, token => $token);
4885                  !!!cp ('t121');              push @{$self->{open_elements}},
4886                }                  [$self->{head_element}, $el_category->{head}];
4887                $self->{head_element_inserted} = 1;
4888                ## NOTE: There is a "as if in head" code clone.            } else {
4889                $script_start_tag->($insert_to_current);              !!!cp ('t121');
4890                pop @{$self->{open_elements}}            }
4891                    if $self->{insertion_mode} == AFTER_HEAD_IM;  
4892                redo B;            ## NOTE: There is a "as if in head" code clone.
4893              } elsif ($token->{tag_name} eq 'body' or            $script_start_tag->();
4894                       $token->{tag_name} eq 'frameset') {            pop @{$self->{open_elements}} # <head>
4895                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4896              next B;
4897            } elsif ($token->{tag_name} eq 'body' or
4898                     $token->{tag_name} eq 'frameset') {
4899                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4900                  !!!cp ('t122');                  !!!cp ('t122');
4901                  ## As if </noscript>                  ## As if </noscript>
4902                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4903                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4904                                    text => $token->{tag_name}, token => $token);
4905                                    
4906                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4907                  ## As if </head>                  ## As if </head>
# Line 3621  sub _tree_construction_main ($) { Line 4918  sub _tree_construction_main ($) {
4918                }                }
4919    
4920                ## "after head" insertion mode                ## "after head" insertion mode
4921                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4922                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4923                  !!!cp ('t126');                  !!!cp ('t126');
4924                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3631  sub _tree_construction_main ($) { Line 4928  sub _tree_construction_main ($) {
4928                } else {                } else {
4929                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4930                }                }
4931                  !!!nack ('t127.1');
4932                !!!next-token;                !!!next-token;
4933                redo B;                next B;
4934              } else {              } else {
4935                !!!cp ('t128');                !!!cp ('t128');
4936                #                #
# Line 3642  sub _tree_construction_main ($) { Line 4940  sub _tree_construction_main ($) {
4940                !!!cp ('t129');                !!!cp ('t129');
4941                ## As if </noscript>                ## As if </noscript>
4942                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4943                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4944                                  text => $token->{tag_name}, token => $token);
4945                                
4946                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4947                ## As if </head>                ## As if </head>
# Line 3661  sub _tree_construction_main ($) { Line 4960  sub _tree_construction_main ($) {
4960    
4961              ## "after head" insertion mode              ## "after head" insertion mode
4962              ## As if <body>              ## As if <body>
4963              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4964              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4965              ## reprocess              ## reprocess
4966              redo B;              !!!ack-later;
4967                next B;
4968            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4969              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4970                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4971                  !!!cp ('t132');                  !!!cp ('t132');
4972                  ## As if <head>                  ## As if <head>
4973                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4974                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4975                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4976                        [$self->{head_element}, $el_category->{head}];
4977    
4978                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4979                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4980                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4981                  !!!next-token;                  !!!next-token;
4982                  redo B;                  next B;
4983                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4984                  !!!cp ('t133');                  !!!cp ('t133');
4985                  ## As if </noscript>                  ## As if </noscript>
4986                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4987                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/',
4988                                    text => 'head', token => $token);
4989                                    
4990                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4991                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4992                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4993                  !!!next-token;                  !!!next-token;
4994                  redo B;                  next B;
4995                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4996                  !!!cp ('t134');                  !!!cp ('t134');
4997                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4998                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4999                  !!!next-token;                  !!!next-token;
5000                  redo B;                  next B;
5001                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5002                    !!!cp ('t134.1');
5003                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5004                                    token => $token);
5005                    ## Ignore the token
5006                    !!!next-token;
5007                    next B;
5008                } else {                } else {
5009                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
5010                }                }
5011              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5012                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3706  sub _tree_construction_main ($) { Line 5014  sub _tree_construction_main ($) {
5014                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5015                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5016                  !!!next-token;                  !!!next-token;
5017                  redo B;                  next B;
5018                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5019                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5020                  !!!cp ('t137');                  !!!cp ('t137');
5021                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag',
5022                                    text => 'noscript', token => $token);
5023                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5024                  !!!next-token;                  !!!next-token;
5025                  redo B;                  next B;
5026                } else {                } else {
5027                  !!!cp ('t138');                  !!!cp ('t138');
5028                  #                  #
# Line 3720  sub _tree_construction_main ($) { Line 5030  sub _tree_construction_main ($) {
5030              } elsif ({              } elsif ({
5031                        body => 1, html => 1,                        body => 1, html => 1,
5032                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5033                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5034                  !!!cp ('t139');                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5035                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5036                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
   
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
5037                  !!!cp ('t140');                  !!!cp ('t140');
5038                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5039                                    text => $token->{tag_name}, token => $token);
5040                  ## Ignore the token                  ## Ignore the token
5041                  !!!next-token;                  !!!next-token;
5042                  redo B;                  next B;
5043                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5044                    !!!cp ('t140.1');
5045                    !!!parse-error (type => 'unmatched end tag',
5046                                    text => $token->{tag_name}, token => $token);
5047                    ## Ignore the token
5048                    !!!next-token;
5049                    next B;
5050                } else {                } else {
5051                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5052                }                }
5053                              } elsif ($token->{tag_name} eq 'p') {
5054                #                !!!cp ('t142');
5055              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5056                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5057                       }->{$token->{tag_name}}) {                ## Ignore the token
5058                  !!!next-token;
5059                  next B;
5060                } elsif ($token->{tag_name} eq 'br') {
5061                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5062                  !!!cp ('t142');                  !!!cp ('t142.2');
5063                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5064                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5065                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5066                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5067      
5068                    ## Reprocess in the "after head" insertion mode...
5069                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5070                    !!!cp ('t143.2');
5071                    ## As if </head>
5072                    pop @{$self->{open_elements}};
5073                    $self->{insertion_mode} = AFTER_HEAD_IM;
5074      
5075                    ## Reprocess in the "after head" insertion mode...
5076                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5077                    !!!cp ('t143.3');
5078                    ## ISSUE: Two parse errors for <head><noscript></br>
5079                    !!!parse-error (type => 'unmatched end tag',
5080                                    text => 'br', token => $token);
5081                    ## As if </noscript>
5082                    pop @{$self->{open_elements}};
5083                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5084    
5085                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5086                } else {                  ## As if </head>
5087                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5088                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5089    
5090                #                  ## Reprocess in the "after head" insertion mode...
5091              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5092                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5093                  #                  #
5094                } else {                } else {
5095                  !!!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;  
5096                }                }
5097    
5098                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5099                  !!!parse-error (type => 'unmatched end tag',
5100                                  text => 'br', token => $token);
5101                  ## Ignore the token
5102                  !!!next-token;
5103                  next B;
5104                } else {
5105                  !!!cp ('t145');
5106                  !!!parse-error (type => 'unmatched end tag',
5107                                  text => $token->{tag_name}, token => $token);
5108                  ## Ignore the token
5109                  !!!next-token;
5110                  next B;
5111              }              }
5112    
5113              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5114                !!!cp ('t146');                !!!cp ('t146');
5115                ## As if </noscript>                ## As if </noscript>
5116                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5117                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5118                                  text => $token->{tag_name}, token => $token);
5119                                
5120                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5121                ## As if </head>                ## As if </head>
# Line 3790  sub _tree_construction_main ($) { Line 5131  sub _tree_construction_main ($) {
5131              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5132  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5133                !!!cp ('t148');                !!!cp ('t148');
5134                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5135                                  text => $token->{tag_name}, token => $token);
5136                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5137                !!!next-token;                !!!next-token;
5138                redo B;                next B;
5139              } else {              } else {
5140                !!!cp ('t149');                !!!cp ('t149');
5141              }              }
5142    
5143              ## "after head" insertion mode              ## "after head" insertion mode
5144              ## As if <body>              ## As if <body>
5145              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5146              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5147              ## reprocess              ## reprocess
5148              redo B;              next B;
5149            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5150              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5151            }            !!!cp ('t149.1');
5152    
5153              ## NOTE: As if <head>
5154              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5155              $self->{open_elements}->[-1]->[0]->append_child
5156                  ($self->{head_element});
5157              #push @{$self->{open_elements}},
5158              #    [$self->{head_element}, $el_category->{head}];
5159              #$self->{insertion_mode} = IN_HEAD_IM;
5160              ## NOTE: Reprocess.
5161    
5162              ## NOTE: As if </head>
5163              #pop @{$self->{open_elements}};
5164              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5165              ## NOTE: Reprocess.
5166              
5167              #
5168            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5169              !!!cp ('t149.2');
5170    
5171              ## NOTE: As if </head>
5172              pop @{$self->{open_elements}};
5173              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5174              ## NOTE: Reprocess.
5175    
5176            ## ISSUE: An issue in the spec.            #
5177            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5178              !!!cp ('t149.3');
5179    
5180              !!!parse-error (type => 'in noscript:#eof', token => $token);
5181    
5182              ## As if </noscript>
5183              pop @{$self->{open_elements}};
5184              #$self->{insertion_mode} = IN_HEAD_IM;
5185              ## NOTE: Reprocess.
5186    
5187              ## NOTE: As if </head>
5188              pop @{$self->{open_elements}};
5189              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5190              ## NOTE: Reprocess.
5191    
5192              #
5193            } else {
5194              !!!cp ('t149.4');
5195              #
5196            }
5197    
5198            ## NOTE: As if <body>
5199            !!!insert-element ('body',, $token);
5200            $self->{insertion_mode} = IN_BODY_IM;
5201            ## NOTE: Reprocess.
5202            next B;
5203          } else {
5204            die "$0: $token->{type}: Unknown token type";
5205          }
5206      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5207            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5208              !!!cp ('t150');              !!!cp ('t150');
# Line 3818  sub _tree_construction_main ($) { Line 5212  sub _tree_construction_main ($) {
5212              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5213    
5214              !!!next-token;              !!!next-token;
5215              redo B;              next B;
5216            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5217              if ({              if ({
5218                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3826  sub _tree_construction_main ($) { Line 5220  sub _tree_construction_main ($) {
5220                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5221                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5222                  ## have an element in table scope                  ## have an element in table scope
5223                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5224                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5225                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5226                      !!!cp ('t151');                      !!!cp ('t151');
5227                      $tn = $node->[1];  
5228                      last INSCOPE;                      ## Close the cell
5229                    } elsif ({                      !!!back-token; # <x>
5230                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5231                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5232                                  line => $token->{line},
5233                                  column => $token->{column}};
5234                        next B;
5235                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5236                      !!!cp ('t152');                      !!!cp ('t152');
5237                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5238                        last;
5239                    }                    }
5240                  } # INSCOPE                  }
5241                    unless (defined $tn) {  
5242                      !!!cp ('t153');                  !!!cp ('t153');
5243  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
5244                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      text => $token->{tag_name}, token => $token);
5245                      ## Ignore the token                  ## Ignore the token
5246                      !!!next-token;                  !!!nack ('t153.1');
5247                      redo B;                  !!!next-token;
5248                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo B;  
5249                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5250                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5251                                    token => $token);
5252                                    
5253                  ## As if </caption>                  ## NOTE: As if </caption>.
5254                  ## have a table element in table scope                  ## have a table element in table scope
5255                  my $i;                  my $i;
5256                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5257                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5258                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5259                      !!!cp ('t155');                      if ($node->[1] & CAPTION_EL) {
5260                      $i = $_;                        !!!cp ('t155');
5261                      last INSCOPE;                        $i = $_;
5262                    } elsif ({                        last INSCOPE;
5263                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5264                             }->{$node->[1]}) {                        !!!cp ('t156');
5265                      !!!cp ('t156');                        last;
5266                      last INSCOPE;                      }
5267                    }                    }
5268    
5269                      !!!cp ('t157');
5270                      !!!parse-error (type => 'start tag not allowed',
5271                                      text => $token->{tag_name}, token => $token);
5272                      ## Ignore the token
5273                      !!!nack ('t157.1');
5274                      !!!next-token;
5275                      next B;
5276                  } # 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;  
                   }  
5277                                    
5278                  ## generate implied end tags                  ## generate implied end tags
5279                  while ({                  while ($self->{open_elements}->[-1]->[1]
5280                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5281                    !!!cp ('t158');                    !!!cp ('t158');
5282                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5283                  }                  }
5284    
5285                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5286                    !!!cp ('t159');                    !!!cp ('t159');
5287                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5288                                      text => $self->{open_elements}->[-1]->[0]
5289                                          ->manakai_local_name,
5290                                      token => $token);
5291                  } else {                  } else {
5292                    !!!cp ('t160');                    !!!cp ('t160');
5293                  }                  }
# Line 3904  sub _tree_construction_main ($) { Line 5299  sub _tree_construction_main ($) {
5299                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5300                                    
5301                  ## reprocess                  ## reprocess
5302                  redo B;                  !!!ack-later;
5303                    next B;
5304                } else {                } else {
5305                  !!!cp ('t161');                  !!!cp ('t161');
5306                  #                  #
# Line 3920  sub _tree_construction_main ($) { Line 5316  sub _tree_construction_main ($) {
5316                  my $i;                  my $i;
5317                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5318                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5319                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5320                      !!!cp ('t163');                      !!!cp ('t163');
5321                      $i = $_;                      $i = $_;
5322                      last INSCOPE;                      last INSCOPE;
5323                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5324                      !!!cp ('t164');                      !!!cp ('t164');
5325                      last INSCOPE;                      last INSCOPE;
5326                    }                    }
5327                  } # INSCOPE                  } # INSCOPE
5328                    unless (defined $i) {                    unless (defined $i) {
5329                      !!!cp ('t165');                      !!!cp ('t165');
5330                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
5331                                        text => $token->{tag_name},
5332                                        token => $token);
5333                      ## Ignore the token                      ## Ignore the token
5334                      !!!next-token;                      !!!next-token;
5335                      redo B;                      next B;
5336                    }                    }
5337                                    
5338                  ## generate implied end tags                  ## generate implied end tags
5339                  while ({                  while ($self->{open_elements}->[-1]->[1]
5340                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5341                    !!!cp ('t166');                    !!!cp ('t166');
5342                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5343                  }                  }
5344    
5345                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5346                            ne $token->{tag_name}) {
5347                    !!!cp ('t167');                    !!!cp ('t167');
5348                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5349                                      text => $self->{open_elements}->[-1]->[0]
5350                                          ->manakai_local_name,
5351                                      token => $token);
5352                  } else {                  } else {
5353                    !!!cp ('t168');                    !!!cp ('t168');
5354                  }                  }
# Line 3961  sub _tree_construction_main ($) { Line 5360  sub _tree_construction_main ($) {
5360                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5361                                    
5362                  !!!next-token;                  !!!next-token;
5363                  redo B;                  next B;
5364                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5365                  !!!cp ('t169');                  !!!cp ('t169');
5366                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5367                                    text => $token->{tag_name}, token => $token);
5368                  ## Ignore the token                  ## Ignore the token
5369                  !!!next-token;                  !!!next-token;
5370                  redo B;                  next B;
5371                } else {                } else {
5372                  !!!cp ('t170');                  !!!cp ('t170');
5373                  #                  #
# Line 3976  sub _tree_construction_main ($) { Line 5376  sub _tree_construction_main ($) {
5376                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5377                  ## have a table element in table scope                  ## have a table element in table scope
5378                  my $i;                  my $i;
5379                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5380                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5381                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5382                      !!!cp ('t171');                      if ($node->[1] & CAPTION_EL) {
5383                      $i = $_;                        !!!cp ('t171');
5384                      last INSCOPE;                        $i = $_;
5385                    } elsif ({                        last INSCOPE;
5386                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5387                             }->{$node->[1]}) {                        !!!cp ('t172');
5388                      !!!cp ('t172');                        last;
5389                      last INSCOPE;                      }
5390                    }                    }
5391    
5392                      !!!cp ('t173');
5393                      !!!parse-error (type => 'unmatched end tag',
5394                                      text => $token->{tag_name}, token => $token);
5395                      ## Ignore the token
5396                      !!!next-token;
5397                      next B;
5398                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5399                                    
5400                  ## generate implied end tags                  ## generate implied end tags
5401                  while ({                  while ($self->{open_elements}->[-1]->[1]
5402                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5403                    !!!cp ('t174');                    !!!cp ('t174');
5404                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5405                  }                  }
5406                                    
5407                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5408                    !!!cp ('t175');                    !!!cp ('t175');
5409                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5410                                      text => $self->{open_elements}->[-1]->[0]
5411                                          ->manakai_local_name,
5412                                      token => $token);
5413                  } else {                  } else {
5414                    !!!cp ('t176');                    !!!cp ('t176');
5415                  }                  }
# Line 4019  sub _tree_construction_main ($) { Line 5421  sub _tree_construction_main ($) {
5421                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5422                                    
5423                  !!!next-token;                  !!!next-token;
5424                  redo B;                  next B;
5425                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5426                  !!!cp ('t177');                  !!!cp ('t177');
5427                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5428                                    text => $token->{tag_name}, token => $token);
5429                  ## Ignore the token                  ## Ignore the token
5430                  !!!next-token;                  !!!next-token;
5431                  redo B;                  next B;
5432                } else {                } else {
5433                  !!!cp ('t178');                  !!!cp ('t178');
5434                  #                  #
# Line 4038  sub _tree_construction_main ($) { Line 5441  sub _tree_construction_main ($) {
5441                ## have an element in table scope                ## have an element in table scope
5442                my $i;                my $i;
5443                my $tn;                my $tn;
5444                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5445                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5446                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5447                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5448                    $i = $_;                      !!!cp ('t179');
5449                    last INSCOPE;                      $i = $_;
5450                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5451                    !!!cp ('t180');                      ## Close the cell
5452                    $tn = $node->[1];                      !!!back-token; # </x>
5453                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5454                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5455                  } elsif ({                                column => $token->{column}};
5456                            table => 1, html => 1,                      next B;
5457                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5458                    !!!cp ('t181');                      !!!cp ('t180');
5459                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
5460                        ## NOTE: There is exactly one |td| or |th| element
5461                        ## in scope in the stack of open elements by definition.
5462                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5463                        ## ISSUE: Can this be reached?
5464                        !!!cp ('t181');
5465                        last;
5466                      }
5467                  }                  }
5468                } # INSCOPE  
               unless (defined $i) {  
5469                  !!!cp ('t182');                  !!!cp ('t182');
5470                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5471                        text => $token->{tag_name}, token => $token);
5472                  ## Ignore the token                  ## Ignore the token
5473                  !!!next-token;                  !!!next-token;
5474                  redo B;                  next B;
5475                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5476              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5477                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5478                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5479                                  token => $token);
5480    
5481                ## As if </caption>                ## As if </caption>
5482                ## have a table element in table scope                ## have a table element in table scope
5483                my $i;                my $i;
5484                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5485                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5486                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5487                    !!!cp ('t184');                    !!!cp ('t184');
5488                    $i = $_;                    $i = $_;
5489                    last INSCOPE;                    last INSCOPE;
5490                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5491                    !!!cp ('t185');                    !!!cp ('t185');
5492                    last INSCOPE;                    last INSCOPE;
5493                  }                  }
5494                } # INSCOPE                } # INSCOPE
5495                unless (defined $i) {                unless (defined $i) {
5496                  !!!cp ('t186');                  !!!cp ('t186');
5497                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag',
5498                                    text => 'caption', token => $token);
5499                  ## Ignore the token                  ## Ignore the token
5500                  !!!next-token;                  !!!next-token;
5501                  redo B;                  next B;
5502                }                }
5503                                
5504                ## generate implied end tags                ## generate implied end tags
5505                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5506                  !!!cp ('t187');                  !!!cp ('t187');
5507                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5508                }                }
5509    
5510                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5511                  !!!cp ('t188');                  !!!cp ('t188');
5512                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5513                                    text => $self->{open_elements}->[-1]->[0]
5514                                        ->manakai_local_name,
5515                                    token => $token);
5516                } else {                } else {
5517                  !!!cp ('t189');                  !!!cp ('t189');
5518                }                }
# Line 4120  sub _tree_construction_main ($) { Line 5524  sub _tree_construction_main ($) {
5524                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5525    
5526                ## reprocess                ## reprocess
5527                redo B;                next B;
5528              } elsif ({              } elsif ({
5529                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5530                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5531                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5532                  !!!cp ('t190');                  !!!cp ('t190');
5533                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5534                                    text => $token->{tag_name}, token => $token);
5535                  ## Ignore the token                  ## Ignore the token
5536                  !!!next-token;                  !!!next-token;
5537                  redo B;                  next B;
5538                } else {                } else {
5539                  !!!cp ('t191');                  !!!cp ('t191');
5540                  #                  #
# Line 4140  sub _tree_construction_main ($) { Line 5545  sub _tree_construction_main ($) {
5545                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5546                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5547                !!!cp ('t192');                !!!cp ('t192');
5548                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5549                                  text => $token->{tag_name}, token => $token);
5550                ## Ignore the token                ## Ignore the token
5551                !!!next-token;                !!!next-token;
5552                redo B;                next B;
5553              } else {              } else {
5554                !!!cp ('t193');                !!!cp ('t193');
5555                #                #
5556              }              }
5557          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5558            for my $entry (@{$self->{open_elements}}) {
5559              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5560                !!!cp ('t75');
5561                !!!parse-error (type => 'in body:#eof', token => $token);
5562                last;
5563              }
5564            }
5565    
5566            ## Stop parsing.
5567            last B;
5568        } else {        } else {
5569          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5570        }        }
# Line 4156  sub _tree_construction_main ($) { Line 5573  sub _tree_construction_main ($) {
5573        #        #
5574      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5575        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5576              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5577                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5578              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5579                                
5580                unless (length $token->{data}) {            unless (length $token->{data}) {
5581                  !!!cp ('t194');              !!!cp ('t194');
5582                  !!!next-token;              !!!next-token;
5583                  redo B;              next B;
5584                } else {            } else {
5585                  !!!cp ('t195');              !!!cp ('t195');
5586                }            }
5587              }          }
5588    
5589              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5590    
5591              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5592              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5593                            
5594              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5595                   table => 1, tbody => 1, tfoot => 1,            # MUST
5596                   thead => 1, tr => 1,            my $foster_parent_element;
5597                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5598                # MUST            my $prev_sibling;
5599                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5600                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5601                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5602                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5603                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5604                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5605                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5606                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5607                      $foster_parent_element = $parent;                  #
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
5608                } else {                } else {
5609                  !!!cp ('t199');                  !!!cp ('t197');
5610                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5611                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5612                     $next_sibling);                  #
5613                }                }
5614              } else {                last OE;
               !!!cp ('t200');  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5615              }              }
5616              } # OE
5617              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5618              $prev_sibling = $foster_parent_element->last_child
5619                  unless defined $foster_parent_element;
5620              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5621              if (defined $prev_sibling and
5622                  $prev_sibling->node_type == 3) {
5623                !!!cp ('t198');
5624                $prev_sibling->manakai_append_text ($token->{data});
5625              } else {
5626                !!!cp ('t199');
5627                $foster_parent_element->insert_before
5628                    ($self->{document}->create_text_node ($token->{data}),
5629                     $next_sibling);
5630              }
5631              $open_tables->[-1]->[1] = 1; # tainted
5632              $open_tables->[-1]->[2] = 1; # ~node inserted
5633            } else {
5634              ## NOTE: Fragment case or in a foster parent'ed element
5635              ## (e.g. |<table><span>a|).  In fragment case, whether the
5636              ## character is appended to existing node or a new node is
5637              ## created is irrelevant, since the foster parent'ed nodes
5638              ## are discarded and fragment parsing does not invoke any
5639              ## script.
5640              !!!cp ('t200');
5641              $self->{open_elements}->[-1]->[0]->manakai_append_text
5642                  ($token->{data});
5643            }
5644                            
5645              !!!next-token;          !!!next-token;
5646              redo B;          next B;
5647        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5648              if ({          if ({
5649                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5650                   th => 1, td => 1,               th => 1, td => 1,
5651                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5652                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5653                  ## Clear back to table context              ## Clear back to table context
5654                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5655                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5656                    !!!cp ('t201');                !!!cp ('t201');
5657                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                pop @{$self->{open_elements}};
5658                    pop @{$self->{open_elements}};              }
5659                  }              
5660                                !!!insert-element ('tbody',, $token);
5661                  !!!insert-element ('tbody');              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5662                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              ## reprocess in the "in table body" insertion mode...
5663                  ## reprocess in the "in table body" insertion mode...            }
5664                }            
5665              if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5666                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {              unless ($token->{tag_name} eq 'tr') {
5667                  unless ($token->{tag_name} eq 'tr') {                !!!cp ('t202');
5668                    !!!cp ('t202');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5669                    !!!parse-error (type => 'missing start tag:tr');              }
                 }  
5670                                    
5671                  ## Clear back to table body context              ## Clear back to table body context
5672                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5673                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5674                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5675                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5676                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5677                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              }
                   pop @{$self->{open_elements}};  
                 }  
5678                                    
5679                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5680                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5681                    !!!cp ('t204');                !!!cp ('t204');
5682                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5683                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5684                    redo B;                !!!nack ('t204');
5685                  } else {                !!!next-token;
5686                    !!!cp ('t205');                next B;
5687                    !!!insert-element ('tr');              } else {
5688                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5689                  }                !!!insert-element ('tr',, $token);
5690                } else {                ## reprocess in the "in row" insertion mode
5691                  !!!cp ('t206');              }
5692                }            } else {
5693                !!!cp ('t206');
5694              }
5695    
5696                ## Clear back to table row context                ## Clear back to table row context
5697                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5698                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5699                  !!!cp ('t207');                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5700                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5701                }                }
5702                                
5703                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5704                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5705              $self->{insertion_mode} = IN_CELL_IM;
5706    
5707                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5708                                
5709              !!!nack ('t207.1');
5710              !!!next-token;
5711              next B;
5712            } elsif ({
5713                      caption => 1, col => 1, colgroup => 1,
5714                      tbody => 1, tfoot => 1, thead => 1,
5715                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5716                     }->{$token->{tag_name}}) {
5717              if ($self->{insertion_mode} == IN_ROW_IM) {
5718                ## As if </tr>
5719                ## have an element in table scope
5720                my $i;
5721                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5722                  my $node = $self->{open_elements}->[$_];
5723                  if ($node->[1] & TABLE_ROW_EL) {
5724                    !!!cp ('t208');
5725                    $i = $_;
5726                    last INSCOPE;
5727                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5728                    !!!cp ('t209');
5729                    last INSCOPE;
5730                  }
5731                } # INSCOPE
5732                unless (defined $i) {
5733                  !!!cp ('t210');
5734                  ## TODO: This type is wrong.
5735                  !!!parse-error (type => 'unmacthed end tag',
5736                                  text => $token->{tag_name}, token => $token);
5737                  ## Ignore the token
5738                  !!!nack ('t210.1');
5739                !!!next-token;                !!!next-token;
5740                redo B;                next B;
5741              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] eq 'tr') {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ({  
                             html => 1,  
   
                             ## NOTE: This element does not appear here, maybe.  
                             table => 1,  
                            }->{$node->[1]}) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                  !!!cp ('t210');  
 ## TODO: This type is wrong.  
                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});  
                   ## Ignore the token  
                   !!!next-token;  
                   redo B;  
                 }  
5742                                    
5743                  ## Clear back to table row context                  ## Clear back to table row context
5744                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5745                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5746                    !!!cp ('t211');                    !!!cp ('t211');
5747                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5748                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5749                  }                  }
5750                                    
# Line 4335  sub _tree_construction_main ($) { Line 5753  sub _tree_construction_main ($) {
5753                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5754                    !!!cp ('t212');                    !!!cp ('t212');
5755                    ## reprocess                    ## reprocess
5756                    redo B;                    !!!ack-later;
5757                      next B;
5758                  } else {                  } else {
5759                    !!!cp ('t213');                    !!!cp ('t213');
5760                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4347  sub _tree_construction_main ($) { Line 5766  sub _tree_construction_main ($) {
5766                  my $i;                  my $i;
5767                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5768                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5769                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5770                      !!!cp ('t214');                      !!!cp ('t214');
5771                      $i = $_;                      $i = $_;
5772                      last INSCOPE;                      last INSCOPE;
5773                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5774                      !!!cp ('t215');                      !!!cp ('t215');
5775                      last INSCOPE;                      last INSCOPE;
5776                    }                    }
5777                  } # INSCOPE                  } # INSCOPE
5778                  unless (defined $i) {                  unless (defined $i) {
5779                    !!!cp ('t216');                    !!!cp ('t216');
5780  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5781                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
5782                                      text => $token->{tag_name}, token => $token);
5783                    ## Ignore the token                    ## Ignore the token
5784                      !!!nack ('t216.1');
5785                    !!!next-token;                    !!!next-token;
5786                    redo B;                    next B;
5787                  }                  }
5788    
5789                  ## Clear back to table body context                  ## Clear back to table body context
5790                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5791                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5792                    !!!cp ('t217');                    !!!cp ('t217');
5793                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5794                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5795                  }                  }
5796                                    
# Line 4393  sub _tree_construction_main ($) { Line 5808  sub _tree_construction_main ($) {
5808                  !!!cp ('t218');                  !!!cp ('t218');
5809                }                }
5810    
5811                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5812                  ## Clear back to table context              ## Clear back to table context
5813                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5814                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5815                    !!!cp ('t219');                !!!cp ('t219');
5816                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5817                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                pop @{$self->{open_elements}};
5818                    pop @{$self->{open_elements}};              }
5819                  }              
5820                                !!!insert-element ('colgroup',, $token);
5821                  !!!insert-element ('colgroup');              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5822                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              ## reprocess
5823                  ## reprocess              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5824                  redo B;              !!!ack-later;
5825                } elsif ({              next B;
5826                          caption => 1,            } elsif ({
5827                          colgroup => 1,                      caption => 1,
5828                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
5829                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
5830                  ## Clear back to table context                     }->{$token->{tag_name}}) {
5831                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              ## Clear back to table context
5832                         $self->{open_elements}->[-1]->[1] ne 'html') {                  while (not ($self->{open_elements}->[-1]->[1]
5833                                    & TABLE_SCOPING_EL)) {
5834                    !!!cp ('t220');                    !!!cp ('t220');
5835                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5836                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5837                  }                  }
5838                                    
5839                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5840                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5841                                    
5842                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5843                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5844                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5845                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5846                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5847                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5848                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5849                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5850                  !!!next-token;                                        }->{$token->{tag_name}};
5851                  redo B;              !!!next-token;
5852                } else {              !!!nack ('t220.1');
5853                  die "$0: in table: <>: $token->{tag_name}";              next B;
5854                }            } else {
5855                die "$0: in table: <>: $token->{tag_name}";
5856              }
5857              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5858                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5859                                  text => $self->{open_elements}->[-1]->[0]
5860                                      ->manakai_local_name,
5861                                  token => $token);
5862    
5863                ## As if </table>                ## As if </table>
5864                ## have a table element in table scope                ## have a table element in table scope
5865                my $i;                my $i;
5866                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5867                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5868                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5869                    !!!cp ('t221');                    !!!cp ('t221');
5870                    $i = $_;                    $i = $_;
5871                    last INSCOPE;                    last INSCOPE;
5872                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5873                    !!!cp ('t222');                    !!!cp ('t222');
5874                    last INSCOPE;                    last INSCOPE;
5875                  }                  }
# Line 4460  sub _tree_construction_main ($) { Line 5877  sub _tree_construction_main ($) {
5877                unless (defined $i) {                unless (defined $i) {
5878                  !!!cp ('t223');                  !!!cp ('t223');
5879  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5880                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5881                                    token => $token);
5882                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5883                    !!!nack ('t223.1');
5884                  !!!next-token;                  !!!next-token;
5885                  redo B;                  next B;
5886                }                }
5887                                
5888    ## TODO: Followings are removed from the latest spec.
5889                ## generate implied end tags                ## generate implied end tags
5890                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5891                  !!!cp ('t224');                  !!!cp ('t224');
5892                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5893                }                }
5894    
5895                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5896                  !!!cp ('t225');                  !!!cp ('t225');
5897  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5898                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5899                                    text => $self->{open_elements}->[-1]->[0]
5900                                        ->manakai_local_name,
5901                                    token => $token);
5902                } else {                } else {
5903                  !!!cp ('t226');                  !!!cp ('t226');
5904                }                }
5905    
5906                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5907                  pop @{$open_tables};
5908    
5909                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5910    
5911                ## reprocess            ## reprocess
5912                redo B;            !!!ack-later;
5913              next B;
5914            } elsif ($token->{tag_name} eq 'style') {
5915              if (not $open_tables->[-1]->[1]) { # tainted
5916                !!!cp ('t227.8');
5917                ## NOTE: This is a "as if in head" code clone.
5918                $parse_rcdata->(CDATA_CONTENT_MODEL);
5919                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5920                next B;
5921              } else {
5922                !!!cp ('t227.7');
5923                #
5924              }
5925            } elsif ($token->{tag_name} eq 'script') {
5926              if (not $open_tables->[-1]->[1]) { # tainted
5927                !!!cp ('t227.6');
5928                ## NOTE: This is a "as if in head" code clone.
5929                $script_start_tag->();
5930                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5931                next B;
5932              } else {
5933                !!!cp ('t227.5');
5934                #
5935              }
5936            } elsif ($token->{tag_name} eq 'input') {
5937              if (not $open_tables->[-1]->[1]) { # tainted
5938                if ($token->{attributes}->{type}) { ## TODO: case
5939                  my $type = lc $token->{attributes}->{type}->{value};
5940                  if ($type eq 'hidden') {
5941                    !!!cp ('t227.3');
5942                    !!!parse-error (type => 'in table',
5943                                    text => $token->{tag_name}, token => $token);
5944    
5945                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5946                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5947    
5948                    ## TODO: form element pointer
5949    
5950                    pop @{$self->{open_elements}};
5951    
5952                    !!!next-token;
5953                    !!!ack ('t227.2.1');
5954                    next B;
5955                  } else {
5956                    !!!cp ('t227.2');
5957                    #
5958                  }
5959                } else {
5960                  !!!cp ('t227.1');
5961                  #
5962                }
5963              } else {
5964                !!!cp ('t227.4');
5965                #
5966              }
5967          } else {          } else {
5968            !!!cp ('t227');            !!!cp ('t227');
           !!!parse-error (type => 'in table:'.$token->{tag_name});  
   
           $insert = $insert_to_foster;  
5969            #            #
5970          }          }
5971    
5972            !!!parse-error (type => 'in table', text => $token->{tag_name},
5973                            token => $token);
5974    
5975            $insert = $insert_to_foster;
5976            #
5977        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5978              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
5979                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 4502  sub _tree_construction_main ($) { Line 5981  sub _tree_construction_main ($) {
5981                my $i;                my $i;
5982                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5983                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5984                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5985                    !!!cp ('t228');                    !!!cp ('t228');
5986                    $i = $_;                    $i = $_;
5987                    last INSCOPE;                    last INSCOPE;
5988                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5989                    !!!cp ('t229');                    !!!cp ('t229');
5990                    last INSCOPE;                    last INSCOPE;
5991                  }                  }
5992                } # INSCOPE                } # INSCOPE
5993                unless (defined $i) {                unless (defined $i) {
5994                  !!!cp ('t230');                  !!!cp ('t230');
5995                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5996                                    text => $token->{tag_name}, token => $token);
5997                  ## Ignore the token                  ## Ignore the token
5998                    !!!nack ('t230.1');
5999                  !!!next-token;                  !!!next-token;
6000                  redo B;                  next B;
6001                } else {                } else {
6002                  !!!cp ('t232');                  !!!cp ('t232');
6003                }                }
6004    
6005                ## Clear back to table row context                ## Clear back to table row context
6006                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6007                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6008                  !!!cp ('t231');                  !!!cp ('t231');
6009  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6011                }                }
6012    
6013                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6014                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6015                !!!next-token;                !!!next-token;
6016                redo B;                !!!nack ('t231.1');
6017                  next B;
6018              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6019                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6020                  ## As if </tr>                  ## As if </tr>
# Line 4544  sub _tree_construction_main ($) { Line 6022  sub _tree_construction_main ($) {
6022                  my $i;                  my $i;
6023                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6024                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6025                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6026                      !!!cp ('t233');                      !!!cp ('t233');
6027                      $i = $_;                      $i = $_;
6028                      last INSCOPE;                      last INSCOPE;
6029                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6030                      !!!cp ('t234');                      !!!cp ('t234');
6031                      last INSCOPE;                      last INSCOPE;
6032                    }                    }
# Line 4558  sub _tree_construction_main ($) { Line 6034  sub _tree_construction_main ($) {
6034                  unless (defined $i) {                  unless (defined $i) {
6035                    !!!cp ('t235');                    !!!cp ('t235');
6036  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6037                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag',
6038                                      text => $token->{type}, token => $token);
6039                    ## Ignore the token                    ## Ignore the token
6040                      !!!nack ('t236.1');
6041                    !!!next-token;                    !!!next-token;
6042                    redo B;                    next B;
6043                  }                  }
6044                                    
6045                  ## Clear back to table row context                  ## Clear back to table row context
6046                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6047                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6048                    !!!cp ('t236');                    !!!cp ('t236');
6049  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6050                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6051                  }                  }
6052                                    
# Line 4584  sub _tree_construction_main ($) { Line 6060  sub _tree_construction_main ($) {
6060                  my $i;                  my $i;
6061                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6062                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6063                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6064                      !!!cp ('t237');                      !!!cp ('t237');
6065                      $i = $_;                      $i = $_;
6066                      last INSCOPE;                      last INSCOPE;
6067                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6068                      !!!cp ('t238');                      !!!cp ('t238');
6069                      last INSCOPE;                      last INSCOPE;
6070                    }                    }
6071                  } # INSCOPE                  } # INSCOPE
6072                  unless (defined $i) {                  unless (defined $i) {
6073                    !!!cp ('t239');                    !!!cp ('t239');
6074                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
6075                                      text => $token->{tag_name}, token => $token);
6076                    ## Ignore the token                    ## Ignore the token
6077                      !!!nack ('t239.1');
6078                    !!!next-token;                    !!!next-token;
6079                    redo B;                    next B;
6080                  }                  }
6081                                    
6082                  ## Clear back to table body context                  ## Clear back to table body context
6083                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6084                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6085                    !!!cp ('t240');                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6086                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6087                  }                  }
6088                                    
# Line 4626  sub _tree_construction_main ($) { Line 6098  sub _tree_construction_main ($) {
6098                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6099                }                }
6100    
6101                  ## NOTE: </table> in the "in table" insertion mode.
6102                  ## When you edit the code fragment below, please ensure that
6103                  ## the code for <table> in the "in table" insertion mode
6104                  ## is synced with it.
6105    
6106                ## have a table element in table scope                ## have a table element in table scope
6107                my $i;                my $i;
6108                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6109                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6110                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6111                    !!!cp ('t241');                    !!!cp ('t241');
6112                    $i = $_;                    $i = $_;
6113                    last INSCOPE;                    last INSCOPE;
6114                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6115                    !!!cp ('t242');                    !!!cp ('t242');
6116                    last INSCOPE;                    last INSCOPE;
6117                  }                  }
6118                } # INSCOPE                } # INSCOPE
6119                unless (defined $i) {                unless (defined $i) {
6120                  !!!cp ('t243');                  !!!cp ('t243');
6121                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6122                                    text => $token->{tag_name}, token => $token);
6123                  ## Ignore the token                  ## Ignore the token
6124                    !!!nack ('t243.1');
6125                  !!!next-token;                  !!!next-token;
6126                  redo B;                  next B;
               }  
   
               ## generate implied end tags  
               while ({  
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!cp ('t244');  
 ## ISSUE: Can this case be reached?  
                 pop @{$self->{open_elements}};  
               }  
                 
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!cp ('t245');  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               } else {  
                 !!!cp ('t246');  
6127                }                }
6128                                    
6129                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6130                  pop @{$open_tables};
6131                                
6132                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6133                                
6134                !!!next-token;                !!!next-token;
6135                redo B;                next B;
6136              } elsif ({              } elsif ({
6137                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6138                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4680  sub _tree_construction_main ($) { Line 6142  sub _tree_construction_main ($) {
6142                  my $i;                  my $i;
6143                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6144                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6145                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6146                      !!!cp ('t247');                      !!!cp ('t247');
6147                      $i = $_;                      $i = $_;
6148                      last INSCOPE;                      last INSCOPE;
6149                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6150                      !!!cp ('t248');                      !!!cp ('t248');
6151                      last INSCOPE;                      last INSCOPE;
6152                    }                    }
6153                  } # INSCOPE                  } # INSCOPE
6154                    unless (defined $i) {                    unless (defined $i) {
6155                      !!!cp ('t249');                      !!!cp ('t249');
6156                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
6157                                        text => $token->{tag_name}, token => $token);
6158                      ## Ignore the token                      ## Ignore the token
6159                        !!!nack ('t249.1');
6160                      !!!next-token;                      !!!next-token;
6161                      redo B;                      next B;
6162                    }                    }
6163                                    
6164                  ## As if </tr>                  ## As if </tr>
# Line 4704  sub _tree_construction_main ($) { Line 6166  sub _tree_construction_main ($) {
6166                  my $i;                  my $i;
6167                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6168                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6169                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6170                      !!!cp ('t250');                      !!!cp ('t250');
6171                      $i = $_;                      $i = $_;
6172                      last INSCOPE;                      last INSCOPE;
6173                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6174                      !!!cp ('t251');                      !!!cp ('t251');
6175                      last INSCOPE;                      last INSCOPE;
6176                    }                    }
6177                  } # INSCOPE                  } # INSCOPE
6178                    unless (defined $i) {                    unless (defined $i) {
6179                      !!!cp ('t252');                      !!!cp ('t252');
6180                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag',
6181                                        text => 'tr', token => $token);
6182                      ## Ignore the token                      ## Ignore the token
6183                        !!!nack ('t252.1');
6184                      !!!next-token;                      !!!next-token;
6185                      redo B;                      next B;
6186                    }                    }
6187                                    
6188                  ## Clear back to table row context                  ## Clear back to table row context
6189                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6190                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6191                    !!!cp ('t253');                    !!!cp ('t253');
6192  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6193                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6194                  }                  }
6195                                    
# Line 4742  sub _tree_construction_main ($) { Line 6202  sub _tree_construction_main ($) {
6202                my $i;                my $i;
6203                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6204                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6205                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6206                    !!!cp ('t254');                    !!!cp ('t254');
6207                    $i = $_;                    $i = $_;
6208                    last INSCOPE;                    last INSCOPE;
6209                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6210                    !!!cp ('t255');                    !!!cp ('t255');
6211                    last INSCOPE;                    last INSCOPE;
6212                  }                  }
6213                } # INSCOPE                } # INSCOPE
6214                unless (defined $i) {                unless (defined $i) {
6215                  !!!cp ('t256');                  !!!cp ('t256');
6216                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6217                                    text => $token->{tag_name}, token => $token);
6218                  ## Ignore the token                  ## Ignore the token
6219                    !!!nack ('t256.1');
6220                  !!!next-token;                  !!!next-token;
6221                  redo B;                  next B;
6222                }                }
6223    
6224                ## Clear back to table body context                ## Clear back to table body context
6225                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6226                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6227                  !!!cp ('t257');                  !!!cp ('t257');
6228  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6229                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6230                }                }
6231    
6232                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6233                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6234                  !!!nack ('t257.1');
6235                !!!next-token;                !!!next-token;
6236                redo B;                next B;
6237              } elsif ({              } elsif ({
6238                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6239                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6240                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6241                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6242                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6243                !!!cp ('t258');            !!!cp ('t258');
6244                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6245                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6246                !!!next-token;            ## Ignore the token
6247                redo B;            !!!nack ('t258.1');
6248               !!!next-token;
6249              next B;
6250          } else {          } else {
6251            !!!cp ('t259');            !!!cp ('t259');
6252            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6253                              text => $token->{tag_name}, token => $token);
6254    
6255            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6256            #            #
6257          }          }
6258          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6259            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6260                    @{$self->{open_elements}} == 1) { # redundant, maybe
6261              !!!parse-error (type => 'in body:#eof', token => $token);
6262              !!!cp ('t259.1');
6263              #
6264            } else {
6265              !!!cp ('t259.2');
6266              #
6267            }
6268    
6269            ## Stop parsing
6270            last B;
6271        } else {        } else {
6272          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6273        }        }
6274      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6275            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6276              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6277                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6278                unless (length $token->{data}) {                unless (length $token->{data}) {
6279                  !!!cp ('t260');                  !!!cp ('t260');
6280                  !!!next-token;                  !!!next-token;
6281                  redo B;                  next B;
6282                }                }
6283              }              }
6284                            
# Line 4812  sub _tree_construction_main ($) { Line 6287  sub _tree_construction_main ($) {
6287            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6288              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6289                !!!cp ('t262');                !!!cp ('t262');
6290                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6291                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6292                  !!!ack ('t262.1');
6293                !!!next-token;                !!!next-token;
6294                redo B;                next B;
6295              } else {              } else {
6296                !!!cp ('t263');                !!!cp ('t263');
6297                #                #
6298              }              }
6299            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6300              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6301                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6302                  !!!cp ('t264');                  !!!cp ('t264');
6303                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag',
6304                                    text => 'colgroup', token => $token);
6305                  ## Ignore the token                  ## Ignore the token
6306                  !!!next-token;                  !!!next-token;
6307                  redo B;                  next B;
6308                } else {                } else {
6309                  !!!cp ('t265');                  !!!cp ('t265');
6310                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6311                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6312                  !!!next-token;                  !!!next-token;
6313                  redo B;                              next B;            
6314                }                }
6315              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6316                !!!cp ('t266');                !!!cp ('t266');
6317                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag',
6318                                  text => 'col', token => $token);
6319                ## Ignore the token                ## Ignore the token
6320                !!!next-token;                !!!next-token;
6321                redo B;                next B;
6322              } else {              } else {
6323                !!!cp ('t267');                !!!cp ('t267');
6324                #                #
6325              }              }
6326            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6327              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6328            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6329              !!!cp ('t270.2');
6330              ## Stop parsing.
6331              last B;
6332            } else {
6333              ## NOTE: As if </colgroup>.
6334              !!!cp ('t270.1');
6335              pop @{$self->{open_elements}}; # colgroup
6336              $self->{insertion_mode} = IN_TABLE_IM;
6337              ## Reprocess.
6338              next B;
6339            }
6340          } else {
6341            die "$0: $token->{type}: Unknown token type";
6342          }
6343    
6344            ## As if </colgroup>            ## As if </colgroup>
6345            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6346              !!!cp ('t269');              !!!cp ('t269');
6347              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
6348                !!!parse-error (type => 'unmatched end tag',
6349                                text => 'colgroup', token => $token);
6350              ## Ignore the token              ## Ignore the token
6351                !!!nack ('t269.1');
6352              !!!next-token;              !!!next-token;
6353              redo B;              next B;
6354            } else {            } else {
6355              !!!cp ('t270');              !!!cp ('t270');
6356              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6357              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6358                !!!ack-later;
6359              ## reprocess              ## reprocess
6360              redo B;              next B;
6361            }            }
6362      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6363        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6364          !!!cp ('t271');          !!!cp ('t271');
6365          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6366          !!!next-token;          !!!next-token;
6367          redo B;          next B;
6368        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6369              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6370                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6371                  !!!cp ('t272');              !!!cp ('t272');
6372                  ## As if </option>              ## As if </option>
6373                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6374                } else {            } else {
6375                  !!!cp ('t273');              !!!cp ('t273');
6376                }            }
6377    
6378                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6379                !!!next-token;            !!!nack ('t273.1');
6380                redo B;            !!!next-token;
6381              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6382                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6383                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6384                  ## As if </option>              !!!cp ('t274');
6385                  pop @{$self->{open_elements}};              ## As if </option>
6386                } else {              pop @{$self->{open_elements}};
6387                  !!!cp ('t275');            } else {
6388                }              !!!cp ('t275');
6389              }
6390    
6391                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6392                  !!!cp ('t276');              !!!cp ('t276');
6393                  ## As if </optgroup>              ## As if </optgroup>
6394                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6395                } else {            } else {
6396                  !!!cp ('t277');              !!!cp ('t277');
6397                }            }
6398    
6399                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6400                !!!next-token;            !!!nack ('t277.1');
6401                redo B;            !!!next-token;
6402              } elsif ($token->{tag_name} eq 'select') {            next B;
6403  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ({
6404                !!!parse-error (type => 'not closed:select');                     select => 1, input => 1, textarea => 1,
6405                ## As if </select> instead                   }->{$token->{tag_name}} or
6406                ## have an element in table scope                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6407                my $i;                    {
6408                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     caption => 1, table => 1,
6409                  my $node = $self->{open_elements}->[$_];                     tbody => 1, tfoot => 1, thead => 1,
6410                  if ($node->[1] eq $token->{tag_name}) {                     tr => 1, td => 1, th => 1,
6411                    !!!cp ('t278');                    }->{$token->{tag_name}})) {
6412                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
6413                    last INSCOPE;            !!!parse-error (type => 'not closed', text => 'select',
6414                  } elsif ({                            token => $token);
6415                            table => 1, html => 1,            ## NOTE: As if the token were </select> (<select> case) or
6416                           }->{$node->[1]}) {            ## as if there were </select> (otherwise).
6417                    !!!cp ('t279');            ## have an element in table scope
6418                    last INSCOPE;            my $i;
6419                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6420                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6421                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6422                  !!!cp ('t280');                !!!cp ('t278');
6423                  !!!parse-error (type => 'unmatched end tag:select');                $i = $_;
6424                  ## Ignore the token                last INSCOPE;
6425                  !!!next-token;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6426                  redo B;                !!!cp ('t279');
6427                }                last INSCOPE;
6428                }
6429              } # INSCOPE
6430              unless (defined $i) {
6431                !!!cp ('t280');
6432                !!!parse-error (type => 'unmatched end tag',
6433                                text => 'select', token => $token);
6434                ## Ignore the token
6435                !!!nack ('t280.1');
6436                !!!next-token;
6437                next B;
6438              }
6439                                
6440                !!!cp ('t281');            !!!cp ('t281');
6441                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6442    
6443                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6444    
6445                !!!next-token;            if ($token->{tag_name} eq 'select') {
6446                redo B;              !!!nack ('t281.2');
6447                !!!next-token;
6448                next B;
6449              } else {
6450                !!!cp ('t281.1');
6451                !!!ack-later;
6452                ## Reprocess the token.
6453                next B;
6454              }
6455          } else {          } else {
6456            !!!cp ('t282');            !!!cp ('t282');
6457            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6458                              text => $token->{tag_name}, token => $token);
6459            ## Ignore the token            ## Ignore the token
6460              !!!nack ('t282.1');
6461            !!!next-token;            !!!next-token;
6462            redo B;            next B;
6463          }          }
6464        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6465              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6466                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6467                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6468                  !!!cp ('t283');              !!!cp ('t283');
6469                  ## As if </option>              ## As if </option>
6470                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6471                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6472                  !!!cp ('t284');              !!!cp ('t284');
6473                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6474                } else {            } else {
6475                  !!!cp ('t285');              !!!cp ('t285');
6476                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6477                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6478                }              ## Ignore the token
6479                !!!next-token;            }
6480                redo B;            !!!nack ('t285.1');
6481              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6482                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6483                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6484                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6485                } else {              !!!cp ('t286');
6486                  !!!cp ('t287');              pop @{$self->{open_elements}};
6487                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6488                  ## Ignore the token              !!!cp ('t287');
6489                }              !!!parse-error (type => 'unmatched end tag',
6490                !!!next-token;                              text => $token->{tag_name}, token => $token);
6491                redo B;              ## Ignore the token
6492              } elsif ($token->{tag_name} eq 'select') {            }
6493                ## have an element in table scope            !!!nack ('t287.1');
6494                my $i;            !!!next-token;
6495                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6496                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6497                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6498                    !!!cp ('t288');            my $i;
6499                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6500                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6501                  } elsif ({              if ($node->[1] & SELECT_EL) {
6502                            table => 1, html => 1,                !!!cp ('t288');
6503                           }->{$node->[1]}) {                $i = $_;
6504                    !!!cp ('t289');                last INSCOPE;
6505                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6506                  }                !!!cp ('t289');
6507                } # INSCOPE                last INSCOPE;
6508                unless (defined $i) {              }
6509                  !!!cp ('t290');            } # INSCOPE
6510                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6511                  ## Ignore the token              !!!cp ('t290');
6512                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6513                  redo B;                              text => $token->{tag_name}, token => $token);
6514                }              ## Ignore the token
6515                !!!nack ('t290.1');
6516                !!!next-token;
6517                next B;
6518              }
6519                                
6520                !!!cp ('t291');            !!!cp ('t291');
6521                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6522    
6523                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6524    
6525                !!!next-token;            !!!nack ('t291.1');
6526                redo B;            !!!next-token;
6527              } elsif ({            next B;
6528                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6529                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6530                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6531                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6532                     }->{$token->{tag_name}}) {
6533  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6534                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6535                              text => $token->{tag_name}, token => $token);
6536                                
6537                ## have an element in table scope            ## have an element in table scope
6538                my $i;            my $i;
6539                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6540                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6541                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6542                    !!!cp ('t292');                !!!cp ('t292');
6543                    $i = $_;                $i = $_;
6544                    last INSCOPE;                last INSCOPE;
6545                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6546                            table => 1, html => 1,                !!!cp ('t293');
6547                           }->{$node->[1]}) {                last INSCOPE;
6548                    !!!cp ('t293');              }
6549                    last INSCOPE;            } # INSCOPE
6550                  }            unless (defined $i) {
6551                } # INSCOPE              !!!cp ('t294');
6552                unless (defined $i) {              ## Ignore the token
6553                  !!!cp ('t294');              !!!nack ('t294.1');
6554                  ## Ignore the token              !!!next-token;
6555                  !!!next-token;              next B;
6556                  redo B;            }
               }  
6557                                
6558                ## As if </select>            ## As if </select>
6559                ## have an element in table scope            ## have an element in table scope
6560                undef $i;            undef $i;
6561                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6562                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6563                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6564                    !!!cp ('t295');                !!!cp ('t295');
6565                    $i = $_;                $i = $_;
6566                    last INSCOPE;                last INSCOPE;
6567                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6568  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6569                    !!!cp ('t296');                !!!cp ('t296');
6570                    last INSCOPE;                last INSCOPE;
6571                  }              }
6572                } # INSCOPE            } # INSCOPE
6573                unless (defined $i) {            unless (defined $i) {
6574                  !!!cp ('t297');              !!!cp ('t297');
6575  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6576                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6577                  ## Ignore the </select> token                              text => 'select', token => $token);
6578                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6579                  redo B;              !!!nack ('t297.1');
6580                }              !!!next-token; ## TODO: ok?
6581                next B;
6582              }
6583                                
6584                !!!cp ('t298');            !!!cp ('t298');
6585                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6586    
6587                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6588    
6589                ## reprocess            !!!ack-later;
6590                redo B;            ## reprocess
6591              next B;
6592          } else {          } else {
6593            !!!cp ('t299');            !!!cp ('t299');
6594            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/',
6595                              text => $token->{tag_name}, token => $token);
6596            ## Ignore the token            ## Ignore the token
6597              !!!nack ('t299.3');
6598            !!!next-token;            !!!next-token;
6599            redo B;            next B;
6600          }          }
6601          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6602            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6603                    @{$self->{open_elements}} == 1) { # redundant, maybe
6604              !!!cp ('t299.1');
6605              !!!parse-error (type => 'in body:#eof', token => $token);
6606            } else {
6607              !!!cp ('t299.2');
6608            }
6609    
6610            ## Stop parsing.
6611            last B;
6612        } else {        } else {
6613          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6614        }        }
6615      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6616        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6617          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6618            my $data = $1;            my $data = $1;
6619            ## As if in body            ## As if in body
6620            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5086  sub _tree_construction_main ($) { Line 6624  sub _tree_construction_main ($) {
6624            unless (length $token->{data}) {            unless (length $token->{data}) {
6625              !!!cp ('t300');              !!!cp ('t300');
6626              !!!next-token;              !!!next-token;
6627              redo B;              next B;
6628            }            }
6629          }          }
6630                    
6631          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6632            !!!cp ('t301');            !!!cp ('t301');
6633            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6634              #
           ## Reprocess in the "after body" insertion mode.  
6635          } else {          } else {
6636            !!!cp ('t302');            !!!cp ('t302');
6637              ## "after body" insertion mode
6638              !!!parse-error (type => 'after body:#text', token => $token);
6639              #
6640          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6641    
6642          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6643          ## reprocess          ## reprocess
6644          redo B;          next B;
6645        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6646          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6647            !!!cp ('t303');            !!!cp ('t303');
6648            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6649                                        text => $token->{tag_name}, token => $token);
6650            ## Reprocess in the "after body" insertion mode.            #
6651          } else {          } else {
6652            !!!cp ('t304');            !!!cp ('t304');
6653              ## "after body" insertion mode
6654              !!!parse-error (type => 'after body',
6655                              text => $token->{tag_name}, token => $token);
6656              #
6657          }          }
6658    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6659          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6660            !!!ack-later;
6661          ## reprocess          ## reprocess
6662          redo B;          next B;
6663        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6664          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6665            !!!cp ('t305');            !!!cp ('t305');
6666            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/',
6667                              text => $token->{tag_name}, token => $token);
6668                        
6669            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6670            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6671              next B;
6672          } else {          } else {
6673            !!!cp ('t306');            !!!cp ('t306');
6674          }          }
# Line 5136  sub _tree_construction_main ($) { Line 6677  sub _tree_construction_main ($) {
6677          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6678            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6679              !!!cp ('t307');              !!!cp ('t307');
6680              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag',
6681                                text => 'html', token => $token);
6682              ## Ignore the token              ## Ignore the token
6683              !!!next-token;              !!!next-token;
6684              redo B;              next B;
6685            } else {            } else {
6686              !!!cp ('t308');              !!!cp ('t308');
6687              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6688              !!!next-token;              !!!next-token;
6689              redo B;              next B;
6690            }            }
6691          } else {          } else {
6692            !!!cp ('t309');            !!!cp ('t309');
6693            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/',
6694                              text => $token->{tag_name}, token => $token);
6695    
6696            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6697            ## reprocess            ## reprocess
6698            redo B;            next B;
6699          }          }
6700          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6701            !!!cp ('t309.2');
6702            ## Stop parsing
6703            last B;
6704        } else {        } else {
6705          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6706        }        }
6707      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6708        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6709          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6710            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6711                        
6712            unless (length $token->{data}) {            unless (length $token->{data}) {
6713              !!!cp ('t310');              !!!cp ('t310');
6714              !!!next-token;              !!!next-token;
6715              redo B;              next B;
6716            }            }
6717          }          }
6718                    
6719          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6720            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721              !!!cp ('t311');              !!!cp ('t311');
6722              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6723            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6724              !!!cp ('t312');              !!!cp ('t312');
6725              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6726            } else { # "after html frameset"            } else { # "after after frameset"
6727              !!!cp ('t313');              !!!cp ('t313');
6728              !!!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');  
6729            }            }
6730                        
6731            ## Ignore the token.            ## Ignore the token.
# Line 5193  sub _tree_construction_main ($) { Line 6736  sub _tree_construction_main ($) {
6736              !!!cp ('t315');              !!!cp ('t315');
6737              !!!next-token;              !!!next-token;
6738            }            }
6739            redo B;            next B;
6740          }          }
6741                    
6742          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6743        } 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');  
         }  
   
6744          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6745              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6746            !!!cp ('t318');            !!!cp ('t318');
6747            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6748              !!!nack ('t318.1');
6749            !!!next-token;            !!!next-token;
6750            redo B;            next B;
6751          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6752                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6753            !!!cp ('t319');            !!!cp ('t319');
6754            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6755            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6756              !!!ack ('t319.1');
6757            !!!next-token;            !!!next-token;
6758            redo B;            next B;
6759          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6760            !!!cp ('t320');            !!!cp ('t320');
6761            ## NOTE: As if in body.            ## NOTE: As if in head.
6762            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6763            redo B;            next B;
6764    
6765              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6766              ## has no parse error.
6767          } else {          } else {
6768            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6769              !!!cp ('t321');              !!!cp ('t321');
6770              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6771            } else {                              text => $token->{tag_name}, token => $token);
6772              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6773              !!!cp ('t322');              !!!cp ('t322');
6774              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset',
6775                                text => $token->{tag_name}, token => $token);
6776              } else { # "after after frameset"
6777                !!!cp ('t322.2');
6778                !!!parse-error (type => 'after after frameset',
6779                                text => $token->{tag_name}, token => $token);
6780            }            }
6781            ## Ignore the token            ## Ignore the token
6782              !!!nack ('t322.1');
6783            !!!next-token;            !!!next-token;
6784            redo B;            next B;
6785          }          }
6786        } 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');  
         }  
   
6787          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6788              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6789            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6790                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6791              !!!cp ('t325');              !!!cp ('t325');
6792              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6793                                text => $token->{tag_name}, token => $token);
6794              ## Ignore the token              ## Ignore the token
6795              !!!next-token;              !!!next-token;
6796            } else {            } else {
# Line 5264  sub _tree_construction_main ($) { Line 6800  sub _tree_construction_main ($) {
6800            }            }
6801    
6802            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6803                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6804              !!!cp ('t327');              !!!cp ('t327');
6805              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6806            } else {            } else {
6807              !!!cp ('t328');              !!!cp ('t328');
6808            }            }
6809            redo B;            next B;
6810          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6811                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6812            !!!cp ('t329');            !!!cp ('t329');
6813            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6814            !!!next-token;            !!!next-token;
6815            redo B;            next B;
6816          } else {          } else {
6817            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6818              !!!cp ('t330');              !!!cp ('t330');
6819              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6820            } else {                              text => $token->{tag_name}, token => $token);
6821              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6822                !!!cp ('t330.1');
6823                !!!parse-error (type => 'after frameset:/',
6824                                text => $token->{tag_name}, token => $token);
6825              } else { # "after after html"
6826              !!!cp ('t331');              !!!cp ('t331');
6827              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after after frameset:/',
6828                                text => $token->{tag_name}, token => $token);
6829            }            }
6830            ## Ignore the token            ## Ignore the token
6831            !!!next-token;            !!!next-token;
6832            redo B;            next B;
6833          }          }
6834          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6835            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6836                    @{$self->{open_elements}} == 1) { # redundant, maybe
6837              !!!cp ('t331.1');
6838              !!!parse-error (type => 'in body:#eof', token => $token);
6839            } else {
6840              !!!cp ('t331.2');
6841            }
6842            
6843            ## Stop parsing
6844            last B;
6845        } else {        } else {
6846          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6847        }        }
   
       ## ISSUE: An issue in spec here  
6848      } else {      } else {
6849        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6850      }      }
# Line 5303  sub _tree_construction_main ($) { Line 6854  sub _tree_construction_main ($) {
6854        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6855          !!!cp ('t332');          !!!cp ('t332');
6856          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6857          $script_start_tag->($insert);          $script_start_tag->();
6858          redo B;          next B;
6859        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6860          !!!cp ('t333');          !!!cp ('t333');
6861          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6862          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6863          redo B;          next B;
6864        } elsif ({        } elsif ({
6865                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6866                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6867          !!!cp ('t334');          !!!cp ('t334');
6868          ## 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
6869          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6870          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6871            !!!ack ('t334.1');
6872          !!!next-token;          !!!next-token;
6873          redo B;          next B;
6874        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6875          ## 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
6876          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6877          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6878    
6879          unless ($self->{confident}) {          unless ($self->{confident}) {
6880            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6881              !!!cp ('t335');              !!!cp ('t335');
6882                ## NOTE: Whether the encoding is supported or not is handled
6883                ## in the {change_encoding} callback.
6884              $self->{change_encoding}              $self->{change_encoding}
6885                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6886                            
6887              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6888                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6889                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6890                                           ->{has_reference});                                           ->{has_reference});
6891            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6892              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6893                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6894                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6895                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6896                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6897                       /x) {
6898                !!!cp ('t336');                !!!cp ('t336');
6899                  ## NOTE: Whether the encoding is supported or not is handled
6900                  ## in the {change_encoding} callback.
6901                $self->{change_encoding}                $self->{change_encoding}
6902                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6903                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6904                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6905                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5367  sub _tree_construction_main ($) { Line 6923  sub _tree_construction_main ($) {
6923            }            }
6924          }          }
6925    
6926            !!!ack ('t338.1');
6927          !!!next-token;          !!!next-token;
6928          redo B;          next B;
6929        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6930          !!!cp ('t341');          !!!cp ('t341');
         !!!parse-error (type => 'in body:title');  
6931          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6932          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6933            if (defined $self->{head_element}) {          next B;
             !!!cp ('t339');  
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             !!!cp ('t340');  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6934        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6935          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6936                                
6937          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6938              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6939            !!!cp ('t342');            !!!cp ('t342');
6940            ## Ignore the token            ## Ignore the token
6941          } else {          } else {
# Line 5401  sub _tree_construction_main ($) { Line 6949  sub _tree_construction_main ($) {
6949              }              }
6950            }            }
6951          }          }
6952            !!!nack ('t343.1');
6953          !!!next-token;          !!!next-token;
6954          redo B;          next B;
6955        } elsif ({        } elsif ({
6956                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6957                  div => 1, dl => 1, fieldset => 1,  
6958                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6959                  listing => 1, menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6960                  pre => 1,                  center => 1, datagrid => 1, details => 1, dialog => 1,
6961                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6962                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6963                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6964                    section => 1, ul => 1,
6965                    ## NOTE: As normal, but drops leading newline
6966                    pre => 1, listing => 1,
6967                    ## NOTE: As normal, but interacts with the form element pointer
6968                    form => 1,
6969                    
6970                    table => 1,
6971                    hr => 1,
6972                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6973            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6974              !!!cp ('t350');
6975              !!!parse-error (type => 'in form:form', token => $token);
6976              ## Ignore the token
6977              !!!nack ('t350.1');
6978              !!!next-token;
6979              next B;
6980            }
6981    
6982          ## has a p element in scope          ## has a p element in scope
6983          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6984            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6985              !!!cp ('t344');              !!!cp ('t344');
6986              !!!back-token;              !!!back-token; # <form>
6987              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6988              redo B;                        line => $token->{line}, column => $token->{column}};
6989            } elsif ({              next B;
6990                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
6991              !!!cp ('t345');              !!!cp ('t345');
6992              last INSCOPE;              last INSCOPE;
6993            }            }
6994          } # INSCOPE          } # INSCOPE
6995                        
6996          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6997          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6998              !!!nack ('t346.1');
6999            !!!next-token;            !!!next-token;
7000            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7001              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5440  sub _tree_construction_main ($) { Line 7008  sub _tree_construction_main ($) {
7008            } else {            } else {
7009              !!!cp ('t348');              !!!cp ('t348');
7010            }            }
7011          } else {          } elsif ($token->{tag_name} eq 'form') {
7012            !!!cp ('t347');            !!!cp ('t347.1');
7013              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7014    
7015              !!!nack ('t347.2');
7016            !!!next-token;            !!!next-token;
7017          }          } elsif ($token->{tag_name} eq 'table') {
7018          redo B;            !!!cp ('t382');
7019        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7020          if (defined $self->{form_element}) {            
7021            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
7022            !!!parse-error (type => 'in form:form');  
7023            ## Ignore the token            !!!nack ('t382.1');
7024              !!!next-token;
7025            } elsif ($token->{tag_name} eq 'hr') {
7026              !!!cp ('t386');
7027              pop @{$self->{open_elements}};
7028            
7029              !!!nack ('t386.1');
7030            !!!next-token;            !!!next-token;
           redo B;  
7031          } else {          } else {
7032            ## 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];  
7033            !!!next-token;            !!!next-token;
           redo B;  
7034          }          }
7035            next B;
7036        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7037          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7038          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7039            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7040              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7041              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7042              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7043              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7044            } elsif ({            ## object (Fx)
7045                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
7046                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
7047                     }->{$_->[1]}) {            ## form (IE), hn (IE)
7048              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7049              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7050            }            ## div (Fx, S)
7051          } # INSCOPE  
7052                      my $non_optional;
         ## Step 1  
7053          my $i = -1;          my $i = -1;
7054          my $node = $self->{open_elements}->[$i];  
7055          LI: {          ## 1.
7056            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7057            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7058              if ($i != -1) {              ## 2. (a) As if </li>
7059                !!!cp ('t355');              {
7060                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7061                                $self->{open_elements}->[-1]->[1]);                #
7062              } else {  
7063                !!!cp ('t356');                ## Otherwise
7064    
7065                  ## 1. generate implied end tags, except for </li>
7066                  #
7067    
7068                  ## 2. If current node != "li", parse error
7069                  if ($non_optional) {
7070                    !!!parse-error (type => 'not closed',
7071                                    text => $non_optional->[0]->manakai_local_name,
7072                                    token => $token);
7073                    !!!cp ('t355');
7074                  } else {
7075                    !!!cp ('t356');
7076                  }
7077    
7078                  ## 3. Pop
7079                  splice @{$self->{open_elements}}, $i;
7080              }              }
7081              splice @{$self->{open_elements}}, $i;  
7082              last LI;              last; ## 2. (b) goto 5.
7083            } else {            } elsif (
7084                       ## NOTE: not "formatting" and not "phrasing"
7085                       ($node->[1] & SPECIAL_EL or
7086                        $node->[1] & SCOPING_EL) and
7087                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7088    
7089                       (not $node->[1] & ADDRESS_EL) &
7090                       (not $node->[1] & DIV_EL) &
7091                       (not $node->[1] & P_EL)) {
7092                ## 3.
7093              !!!cp ('t357');              !!!cp ('t357');
7094            }              last; ## goto 5.
7095                        } 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') {  
7096              !!!cp ('t358');              !!!cp ('t358');
7097              last LI;              #
7098              } else {
7099                !!!cp ('t359');
7100                $non_optional ||= $node;
7101                #
7102            }            }
7103                        ## 4.
7104            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7105            $i--;            $i--;
7106            $node = $self->{open_elements}->[$i];          }
7107            redo LI;  
7108          } # 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  
7109          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7110            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7111              !!!cp ('t360');              !!!cp ('t353');
7112              !!!back-token;  
7113              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7114              redo B;  
7115            } elsif ({              !!!back-token; # <x>
7116                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7117                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7118                     }->{$_->[1]}) {              next B;
7119              !!!cp ('t361');            } elsif ($_->[1] & SCOPING_EL) {
7120                !!!cp ('t354');
7121              last INSCOPE;              last INSCOPE;
7122            }            }
7123          } # INSCOPE          } # INSCOPE
7124              
7125          ## Step 1          ## 5. (b) insert
7126            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7127            !!!nack ('t359.1');
7128            !!!next-token;
7129            next B;
7130          } elsif ($token->{tag_name} eq 'dt' or
7131                   $token->{tag_name} eq 'dd') {
7132            ## NOTE: As normal, but imply </dt> or </dd> when ...
7133    
7134            my $non_optional;
7135          my $i = -1;          my $i = -1;
7136          my $node = $self->{open_elements}->[$i];  
7137          LI: {          ## 1.
7138            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7139            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7140              if ($i != -1) {              ## 2. (a) As if </li>
7141                !!!cp ('t362');              {
7142                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7143                                $self->{open_elements}->[-1]->[1]);                #
7144              } else {  
7145                !!!cp ('t363');                ## Otherwise
7146    
7147                  ## 1. generate implied end tags, except for </dt> or </dd>
7148                  #
7149    
7150                  ## 2. If current node != "dt"|"dd", parse error
7151                  if ($non_optional) {
7152                    !!!parse-error (type => 'not closed',
7153                                    text => $non_optional->[0]->manakai_local_name,
7154                                    token => $token);
7155                    !!!cp ('t355.1');
7156                  } else {
7157                    !!!cp ('t356.1');
7158                  }
7159    
7160                  ## 3. Pop
7161                  splice @{$self->{open_elements}}, $i;
7162              }              }
7163              splice @{$self->{open_elements}}, $i;  
7164              last LI;              last; ## 2. (b) goto 5.
7165              } elsif (
7166                       ## NOTE: not "formatting" and not "phrasing"
7167                       ($node->[1] & SPECIAL_EL or
7168                        $node->[1] & SCOPING_EL) and
7169                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7170    
7171                       (not $node->[1] & ADDRESS_EL) &
7172                       (not $node->[1] & DIV_EL) &
7173                       (not $node->[1] & P_EL)) {
7174                ## 3.
7175                !!!cp ('t357.1');
7176                last; ## goto 5.
7177              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7178                !!!cp ('t358.1');
7179                #
7180            } else {            } else {
7181              !!!cp ('t364');              !!!cp ('t359.1');
7182            }              $non_optional ||= $node;
7183                          #
           ## 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;  
7184            }            }
7185                        ## 4.
7186            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7187            $i--;            $i--;
7188            $node = $self->{open_elements}->[$i];          }
7189            redo LI;  
7190          } # LI          ## 5. (a) has a |p| element in scope
7191                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7192          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] & P_EL) {
7193                !!!cp ('t353.1');
7194                !!!back-token; # <x>
7195                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7196                          line => $token->{line}, column => $token->{column}};
7197                next B;
7198              } elsif ($_->[1] & SCOPING_EL) {
7199                !!!cp ('t354.1');
7200                last INSCOPE;
7201              }
7202            } # INSCOPE
7203    
7204            ## 5. (b) insert
7205            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7206            !!!nack ('t359.2');
7207          !!!next-token;          !!!next-token;
7208          redo B;          next B;
7209        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7210            ## NOTE: As normal, but effectively ends parsing
7211    
7212          ## has a p element in scope          ## has a p element in scope
7213          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7214            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7215              !!!cp ('t367');              !!!cp ('t367');
7216              !!!back-token;              !!!back-token; # <plaintext>
7217              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7218              redo B;                        line => $token->{line}, column => $token->{column}};
7219            } elsif ({              next B;
7220                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7221              !!!cp ('t368');              !!!cp ('t368');
7222              last INSCOPE;              last INSCOPE;
7223            }            }
7224          } # INSCOPE          } # INSCOPE
7225                        
7226          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227                        
7228          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7229                        
7230            !!!nack ('t368.1');
7231          !!!next-token;          !!!next-token;
7232          redo B;          next B;
7233        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7234          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7235            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7236            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7237              !!!cp ('t371');              !!!cp ('t371');
7238              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
7239                            
7240              !!!back-token;              !!!back-token; # <a>
7241              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7242              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7243                $formatting_end_tag->($token);
7244                            
7245              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7246                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5643  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          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7270    
7271            !!!nack ('t374.1');
7272          !!!next-token;          !!!next-token;
7273          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;  
7274        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7275          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7276    
7277          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7278          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7279            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7280            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7281              !!!cp ('t376');              !!!cp ('t376');
7282              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7283              !!!back-token;              !!!back-token; # <nobr>
7284              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7285              redo B;                        line => $token->{line}, column => $token->{column}};
7286            } elsif ({              next B;
7287                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7288              !!!cp ('t377');              !!!cp ('t377');
7289              last INSCOPE;              last INSCOPE;
7290            }            }
7291          } # INSCOPE          } # INSCOPE
7292                    
7293          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7294          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7295                    
7296            !!!nack ('t377.1');
7297          !!!next-token;          !!!next-token;
7298          redo B;          next B;
7299        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7300          ## has a button element in scope          ## has a button element in scope
7301          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7302            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7303            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7304              !!!cp ('t378');              !!!cp ('t378');
7305              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7306              !!!back-token;              !!!back-token; # <button>
7307              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7308              redo B;                        line => $token->{line}, column => $token->{column}};
7309            } elsif ({              next B;
7310                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7311              !!!cp ('t379');              !!!cp ('t379');
7312              last INSCOPE;              last INSCOPE;
7313            }            }
# Line 5708  sub _tree_construction_main ($) { Line 7315  sub _tree_construction_main ($) {
7315                        
7316          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7317                        
7318          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7319    
7320          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7321    
7322          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7323    
7324            !!!nack ('t379.1');
7325          !!!next-token;          !!!next-token;
7326          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, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!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});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo B;  
7327        } elsif ({        } elsif ({
7328                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7329                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7330                  image => 1,                  noembed => 1,
7331                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7332                    noscript => 0, ## TODO: 1 if scripting is enabled
7333                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7334          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7335            !!!cp ('t384');            !!!cp ('t381');
7336            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7337          } else {          } else {
7338            !!!cp ('t385');            !!!cp ('t399');
7339          }          }
7340            ## NOTE: There is an "as if in body" code clone.
7341          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7342          $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;  
7343        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7344          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7345                    
7346          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7347            !!!cp ('t389');            !!!cp ('t389');
7348            ## Ignore the token            ## Ignore the token
7349              !!!nack ('t389'); ## NOTE: Not acknowledged.
7350            !!!next-token;            !!!next-token;
7351            redo B;            next B;
7352          } else {          } else {
7353              !!!ack ('t391.1');
7354    
7355            my $at = $token->{attributes};            my $at = $token->{attributes};
7356            my $form_attrs;            my $form_attrs;
7357            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5825  sub _tree_construction_main ($) { Line 7361  sub _tree_construction_main ($) {
7361            delete $at->{prompt};            delete $at->{prompt};
7362            my @tokens = (            my @tokens = (
7363                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7364                           attributes => $form_attrs},                           attributes => $form_attrs,
7365                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7366                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7367                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7368                            {type => START_TAG_TOKEN, tag_name => 'p',
7369                             line => $token->{line}, column => $token->{column}},
7370                            {type => START_TAG_TOKEN, tag_name => 'label',
7371                             line => $token->{line}, column => $token->{column}},
7372                         );                         );
7373            if ($prompt_attr) {            if ($prompt_attr) {
7374              !!!cp ('t390');              !!!cp ('t390');
7375              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7376                               #line => $token->{line}, column => $token->{column},
7377                              };
7378            } else {            } else {
7379              !!!cp ('t391');              !!!cp ('t391');
7380              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7381                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7382                               #line => $token->{line}, column => $token->{column},
7383                              }; # SHOULD
7384              ## TODO: make this configurable              ## TODO: make this configurable
7385            }            }
7386            push @tokens,            push @tokens,
7387                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7388                             line => $token->{line}, column => $token->{column}},
7389                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7390                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7391                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7392                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7393                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7394            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7395                             line => $token->{line}, column => $token->{column}},
7396                            {type => END_TAG_TOKEN, tag_name => 'form',
7397                             line => $token->{line}, column => $token->{column}};
7398            !!!back-token (@tokens);            !!!back-token (@tokens);
7399            redo B;            !!!next-token;
7400              next B;
7401          }          }
7402        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7403          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7404          my $el;          my $el;
7405          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7406                    
7407          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7408          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 5862  sub _tree_construction_main ($) { Line 7411  sub _tree_construction_main ($) {
7411          $insert->($el);          $insert->($el);
7412                    
7413          my $text = '';          my $text = '';
7414            !!!nack ('t392.1');
7415          !!!next-token;          !!!next-token;
7416          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7417            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
# Line 5892  sub _tree_construction_main ($) { Line 7442  sub _tree_construction_main ($) {
7442            ## Ignore the token            ## Ignore the token
7443          } else {          } else {
7444            !!!cp ('t398');            !!!cp ('t398');
7445            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7446          }          }
7447          !!!next-token;          !!!next-token;
7448            next B;
7449          } elsif ($token->{tag_name} eq 'optgroup' or
7450                   $token->{tag_name} eq 'option') {
7451            ## has an |option| element in scope
7452            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7453              my $node = $self->{open_elements}->[$_];
7454              if ($node->[1] & OPTION_EL) {
7455                !!!cp ('t397.1');
7456                ## NOTE: As if </option>
7457                !!!back-token; # <option> or <optgroup>
7458                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7459                          line => $token->{line}, column => $token->{column}};
7460                next B;
7461              } elsif ($node->[1] & SCOPING_EL) {
7462                !!!cp ('t397.2');
7463                last INSCOPE;
7464              }
7465            } # INSCOPE
7466    
7467            $reconstruct_active_formatting_elements->($insert_to_current);
7468    
7469            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7470    
7471            !!!nack ('t397.3');
7472            !!!next-token;
7473          redo B;          redo B;
7474        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7475                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7476                  noembed => 1,          ## has a |ruby| element in scope
7477                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7478                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7479                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7480          !!!cp ('t399');              !!!cp ('t398.1');
7481          ## NOTE: There is an "as if in body" code clone.              ## generate implied end tags
7482          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7483                  !!!cp ('t398.2');
7484                  pop @{$self->{open_elements}};
7485                }
7486                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7487                  !!!cp ('t398.3');
7488                  !!!parse-error (type => 'not closed',
7489                                  text => $self->{open_elements}->[-1]->[0]
7490                                      ->manakai_local_name,
7491                                  token => $token);
7492                  pop @{$self->{open_elements}}
7493                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7494                }
7495                last INSCOPE;
7496              } elsif ($node->[1] & SCOPING_EL) {
7497                !!!cp ('t398.4');
7498                last INSCOPE;
7499              }
7500            } # INSCOPE
7501    
7502            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7503    
7504            !!!nack ('t398.5');
7505            !!!next-token;
7506          redo B;          redo B;
7507        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7508          !!!cp ('t400');                 $token->{tag_name} eq 'svg') {
7509          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7510    
7511          ## TODO: associate with $self->{form_element} if defined          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7512    
7513            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7514    
7515            ## "adjust foreign attributes" - done in insert-element-f
7516                    
7517          $self->{insertion_mode} = IN_SELECT_IM;          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7518            
7519            if ($self->{self_closing}) {
7520              pop @{$self->{open_elements}};
7521              !!!ack ('t398.6');
7522            } else {
7523              !!!cp ('t398.7');
7524              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7525              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7526              ## mode, "in body" (not "in foreign content") secondary insertion
7527              ## mode, maybe.
7528            }
7529    
7530          !!!next-token;          !!!next-token;
7531          redo B;          next B;
7532        } elsif ({        } elsif ({
7533                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7534                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7535                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7536                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7537                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7538          !!!cp ('t401');          !!!cp ('t401');
7539          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body',
7540                            text => $token->{tag_name}, token => $token);
7541          ## Ignore the token          ## Ignore the token
7542            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7543            !!!next-token;
7544            next B;
7545          } elsif ($token->{tag_name} eq 'param' or
7546                   $token->{tag_name} eq 'source') {
7547            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7548            pop @{$self->{open_elements}};
7549    
7550            !!!ack ('t398.5');
7551          !!!next-token;          !!!next-token;
7552          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7553        } else {        } else {
7554          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
7555              !!!cp ('t384');
7556              !!!parse-error (type => 'image', token => $token);
7557              $token->{tag_name} = 'img';
7558            } else {
7559              !!!cp ('t385');
7560            }
7561    
7562            ## NOTE: There is an "as if <br>" code clone.
7563          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7564                    
7565          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7566    
7567            if ({
7568                 applet => 1, marquee => 1, object => 1,
7569                }->{$token->{tag_name}}) {
7570              !!!cp ('t380');
7571              push @$active_formatting_elements, ['#marker', ''];
7572              !!!nack ('t380.1');
7573            } elsif ({
7574                      b => 1, big => 1, em => 1, font => 1, i => 1,
7575                      s => 1, small => 1, strike => 1,
7576                      strong => 1, tt => 1, u => 1,
7577                     }->{$token->{tag_name}}) {
7578              !!!cp ('t375');
7579              push @$active_formatting_elements, $self->{open_elements}->[-1];
7580              !!!nack ('t375.1');
7581            } elsif ($token->{tag_name} eq 'input') {
7582              !!!cp ('t388');
7583              ## TODO: associate with $self->{form_element} if defined
7584              pop @{$self->{open_elements}};
7585              !!!ack ('t388.2');
7586            } elsif ({
7587                      area => 1, basefont => 1, bgsound => 1, br => 1,
7588                      embed => 1, img => 1, spacer => 1, wbr => 1,
7589                     }->{$token->{tag_name}}) {
7590              !!!cp ('t388.1');
7591              pop @{$self->{open_elements}};
7592              !!!ack ('t388.3');
7593            } elsif ($token->{tag_name} eq 'select') {
7594              ## TODO: associate with $self->{form_element} if defined
7595            
7596              if ($self->{insertion_mode} & TABLE_IMS or
7597                  $self->{insertion_mode} & BODY_TABLE_IMS or
7598                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7599                !!!cp ('t400.1');
7600                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7601              } else {
7602                !!!cp ('t400.2');
7603                $self->{insertion_mode} = IN_SELECT_IM;
7604              }
7605              !!!nack ('t400.3');
7606            } else {
7607              !!!nack ('t402');
7608            }
7609                    
7610          !!!next-token;          !!!next-token;
7611          redo B;          next B;
7612        }        }
7613      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7614        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7615          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7616              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7617            for (@{$self->{open_elements}}) {          INSCOPE: {
7618              unless ({            for (reverse @{$self->{open_elements}}) {
7619                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7620                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7621                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7622                      }->{$_->[1]}) {                last INSCOPE;
7623                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7624                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7625              } else {                last;
               !!!cp ('t404');  
7626              }              }
7627            }            }
7628    
7629            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7630            !!!next-token;  
7631            redo B;            !!!parse-error (type => 'unmatched end tag',
7632          } else {                            text => $token->{tag_name}, token => $token);
7633            !!!cp ('t405');            ## NOTE: Ignore the token.
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
7634            !!!next-token;            !!!next-token;
7635            redo B;            next B;
7636            } # INSCOPE
7637    
7638            for (@{$self->{open_elements}}) {
7639              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7640                !!!cp ('t403');
7641                !!!parse-error (type => 'not closed',
7642                                text => $_->[0]->manakai_local_name,
7643                                token => $token);
7644                last;
7645              } else {
7646                !!!cp ('t404');
7647              }
7648          }          }
7649    
7650            $self->{insertion_mode} = AFTER_BODY_IM;
7651            !!!next-token;
7652            next B;
7653        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7654          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
7655            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7656            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7657                $self->{open_elements}->[1]->[1] & BODY_EL) {
7658              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7659              !!!cp ('t406');              !!!cp ('t406');
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 ('t407');              !!!cp ('t407');
7666            }            }
7667            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7668            ## reprocess            ## reprocess
7669            redo B;            next B;
7670          } else {          } else {
7671            !!!cp ('t408');            !!!cp ('t408');
7672            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7673                              text => $token->{tag_name}, token => $token);
7674            ## Ignore the token            ## Ignore the token
7675            !!!next-token;            !!!next-token;
7676            redo B;            next B;
7677          }          }
7678        } elsif ({        } elsif ({
7679                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7680                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7681                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7682                    address => 1, article => 1, aside => 1, blockquote => 1,
7683                    center => 1, datagrid => 1, details => 1, dialog => 1,
7684                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7685                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7686                    ol => 1, pre => 1, section => 1, ul => 1,
7687    
7688                    ## NOTE: As normal, but ... optional tags
7689                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7690                  button => 1, marquee => 1, object => 1,  
7691                    applet => 1, button => 1, marquee => 1, object => 1,
7692                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7693            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7694            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7695            ## </dd>" code.
7696    
7697          ## has an element in scope          ## has an element in scope
7698          my $i;          my $i;
7699          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7700            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7701            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7702              !!!cp ('t410');              !!!cp ('t410');
7703              $i = $_;              $i = $_;
7704              last INSCOPE;              last INSCOPE;
7705            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7706              !!!cp ('t411');              !!!cp ('t411');
7707              last INSCOPE;              last INSCOPE;
7708            }            }
# Line 6011  sub _tree_construction_main ($) { Line 7710  sub _tree_construction_main ($) {
7710    
7711          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7712            !!!cp ('t413');            !!!cp ('t413');
7713            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7714                              text => $token->{tag_name}, token => $token);
7715              ## NOTE: Ignore the token.
7716          } else {          } else {
7717            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7718            while ({            while ({
7719                      ## END_TAG_OPTIONAL_EL
7720                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7721                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7722                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7723                      option => 1,
7724                      optgroup => 1,
7725                    p => 1,                    p => 1,
7726                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7727                      rp => 1,
7728                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7729              !!!cp ('t409');              !!!cp ('t409');
7730              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7731            }            }
7732    
7733            ## Step 2.            ## Step 2.
7734            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7735                      ne $token->{tag_name}) {
7736              !!!cp ('t412');              !!!cp ('t412');
7737              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7738                                text => $self->{open_elements}->[-1]->[0]
7739                                    ->manakai_local_name,
7740                                token => $token);
7741            } else {            } else {
7742              !!!cp ('t414');              !!!cp ('t414');
7743            }            }
# Line 6038  sub _tree_construction_main ($) { Line 7748  sub _tree_construction_main ($) {
7748            ## Step 4.            ## Step 4.
7749            $clear_up_to_marker->()            $clear_up_to_marker->()
7750                if {                if {
7751                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7752                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7753          }          }
7754          !!!next-token;          !!!next-token;
7755          redo B;          next B;
7756        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7757            ## NOTE: As normal, but interacts with the form element pointer
7758    
7759          undef $self->{form_element};          undef $self->{form_element};
7760    
7761          ## has an element in scope          ## has an element in scope
7762          my $i;          my $i;
7763          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7764            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7765            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7766              !!!cp ('t418');              !!!cp ('t418');
7767              $i = $_;              $i = $_;
7768              last INSCOPE;              last INSCOPE;
7769            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7770              !!!cp ('t419');              !!!cp ('t419');
7771              last INSCOPE;              last INSCOPE;
7772            }            }
# Line 6065  sub _tree_construction_main ($) { Line 7774  sub _tree_construction_main ($) {
7774    
7775          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7776            !!!cp ('t421');            !!!cp ('t421');
7777            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7778                              text => $token->{tag_name}, token => $token);
7779              ## NOTE: Ignore the token.
7780          } else {          } else {
7781            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7782            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7783              !!!cp ('t417');              !!!cp ('t417');
7784              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7785            }            }
7786                        
7787            ## Step 2.            ## Step 2.
7788            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7789                      ne $token->{tag_name}) {
7790              !!!cp ('t417.1');              !!!cp ('t417.1');
7791              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7792                                text => $self->{open_elements}->[-1]->[0]
7793                                    ->manakai_local_name,
7794                                token => $token);
7795            } else {            } else {
7796              !!!cp ('t420');              !!!cp ('t420');
7797            }              }  
# Line 6088  sub _tree_construction_main ($) { Line 7801  sub _tree_construction_main ($) {
7801          }          }
7802    
7803          !!!next-token;          !!!next-token;
7804          redo B;          next B;
7805        } elsif ({        } elsif ({
7806                    ## NOTE: As normal, except acts as a closer for any ...
7807                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7808                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7809          ## has an element in scope          ## has an element in scope
7810          my $i;          my $i;
7811          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7812            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7813            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7814              !!!cp ('t423');              !!!cp ('t423');
7815              $i = $_;              $i = $_;
7816              last INSCOPE;              last INSCOPE;
7817            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7818              !!!cp ('t424');              !!!cp ('t424');
7819              last INSCOPE;              last INSCOPE;
7820            }            }
# Line 6113  sub _tree_construction_main ($) { Line 7822  sub _tree_construction_main ($) {
7822    
7823          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7824            !!!cp ('t425.1');            !!!cp ('t425.1');
7825            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7826                              text => $token->{tag_name}, token => $token);
7827              ## NOTE: Ignore the token.
7828          } else {          } else {
7829            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7830            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7831              !!!cp ('t422');              !!!cp ('t422');
7832              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7833            }            }
7834                        
7835            ## Step 2.            ## Step 2.
7836            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7837                      ne $token->{tag_name}) {
7838              !!!cp ('t425');              !!!cp ('t425');
7839              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
7840                                text => $token->{tag_name}, token => $token);
7841            } else {            } else {
7842              !!!cp ('t426');              !!!cp ('t426');
7843            }            }
# Line 6136  sub _tree_construction_main ($) { Line 7847  sub _tree_construction_main ($) {
7847          }          }
7848                    
7849          !!!next-token;          !!!next-token;
7850          redo B;          next B;
7851        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7852            ## NOTE: As normal, except </p> implies <p> and ...
7853    
7854          ## has an element in scope          ## has an element in scope
7855            my $non_optional;
7856          my $i;          my $i;
7857          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7858            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7859            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7860              !!!cp ('t410.1');              !!!cp ('t410.1');
7861              $i = $_;              $i = $_;
7862              last INSCOPE;              last INSCOPE;
7863            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7864              !!!cp ('t411.1');              !!!cp ('t411.1');
7865              last INSCOPE;              last INSCOPE;
7866              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7867                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7868                !!!cp ('t411.2');
7869                #
7870              } else {
7871                !!!cp ('t411.3');
7872                $non_optional ||= $node;
7873                #
7874            }            }
7875          } # INSCOPE          } # INSCOPE
7876    
7877          if (defined $i) {          if (defined $i) {
7878            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7879              #
7880    
7881              ## 2. If current node != "p", parse error
7882              if ($non_optional) {
7883              !!!cp ('t412.1');              !!!cp ('t412.1');
7884              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7885                                text => $non_optional->[0]->manakai_local_name,
7886                                token => $token);
7887            } else {            } else {
7888              !!!cp ('t414.1');              !!!cp ('t414.1');
7889            }            }
7890    
7891              ## 3. Pop
7892            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7893          } else {          } else {
7894            !!!cp ('t413.1');            !!!cp ('t413.1');
7895            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7896                              text => $token->{tag_name}, token => $token);
7897    
7898            !!!cp ('t415.1');            !!!cp ('t415.1');
7899            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7900            my $el;            my $el;
7901            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7902            $insert->($el);            $insert->($el);
7903            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7904          }          }
7905    
7906          !!!next-token;          !!!next-token;
7907          redo B;          next B;
7908        } elsif ({        } elsif ({
7909                  a => 1,                  a => 1,
7910                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7911                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7912                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7913                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7914          !!!cp ('t427');          !!!cp ('t427');
7915          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7916          redo B;          next B;
7917        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7918          !!!cp ('t428');          !!!cp ('t428');
7919          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag',
7920                            text => 'br', token => $token);
7921    
7922          ## As if <br>          ## As if <br>
7923          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7924                    
7925          my $el;          my $el;
7926          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7927          $insert->($el);          $insert->($el);
7928                    
7929          ## Ignore the token.          ## Ignore the token.
7930          !!!next-token;          !!!next-token;
7931          redo B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7932        } else {        } else {
7933            if ($token->{tag_name} eq 'sarcasm') {
7934              sleep 0.001; # take a deep breath
7935            }
7936    
7937          ## Step 1          ## Step 1
7938          my $node_i = -1;          my $node_i = -1;
7939          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7940    
7941          ## Step 2          ## Step 2
7942          S2: {          S2: {
7943            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7944              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7945              if ($node_tag_name eq $token->{tag_name}) {
7946              ## Step 1              ## Step 1
7947              ## generate implied end tags              ## generate implied end tags
7948              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7949                !!!cp ('t430');                !!!cp ('t430');
7950                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7951                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7952                  ## which seems wrong.
7953                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7954                  $node_i++;
7955              }              }
7956                    
7957              ## Step 2              ## Step 2
7958              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7959                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7960                $current_tag_name =~ tr/A-Z/a-z/;
7961                if ($current_tag_name ne $token->{tag_name}) {
7962                !!!cp ('t431');                !!!cp ('t431');
7963                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7964                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7965                                  text => $self->{open_elements}->[-1]->[0]
7966                                      ->manakai_local_name,
7967                                  token => $token);
7968              } else {              } else {
7969                !!!cp ('t432');                !!!cp ('t432');
7970              }              }
7971                            
7972              ## Step 3              ## Step 3
7973              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7974    
7975              !!!next-token;              !!!next-token;
7976              last S2;              last S2;
7977            } else {            } else {
7978              ## Step 3              ## Step 3
7979              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7980                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7981                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7982                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7983                !!!cp ('t433');                !!!cp ('t433');
7984                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
7985                                  text => $token->{tag_name}, token => $token);
7986                ## Ignore the token                ## Ignore the token
7987                !!!next-token;                !!!next-token;
7988                last S2;                last S2;
             }  
7989    
7990                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7991                  ## 9.27, "a" is a child of <dd> (conforming).  In
7992                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7993                  ## "a" is a child of both <body> and <dd>.
7994                }
7995                
7996              !!!cp ('t434');              !!!cp ('t434');
7997            }            }
7998                        
# Line 6276  sub _tree_construction_main ($) { Line 8003  sub _tree_construction_main ($) {
8003            ## Step 5;            ## Step 5;
8004            redo S2;            redo S2;
8005          } # S2          } # S2
8006          redo B;          next B;
8007        }        }
8008      }      }
8009      redo B;      next B;
8010      } continue { # B
8011        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8012          ## NOTE: The code below is executed in cases where it does not have
8013          ## to be, but it it is harmless even in those cases.
8014          ## has an element in scope
8015          INSCOPE: {
8016            for (reverse 0..$#{$self->{open_elements}}) {
8017              my $node = $self->{open_elements}->[$_];
8018              if ($node->[1] & FOREIGN_EL) {
8019                last INSCOPE;
8020              } elsif ($node->[1] & SCOPING_EL) {
8021                last;
8022              }
8023            }
8024            
8025            ## NOTE: No foreign element in scope.
8026            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8027          } # INSCOPE
8028        }
8029    } # B    } # B
8030    
8031    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6287  sub _tree_construction_main ($) { Line 8033  sub _tree_construction_main ($) {
8033    ## TODO: script stuffs    ## TODO: script stuffs
8034  } # _tree_construct_main  } # _tree_construct_main
8035    
8036  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8037    my $class = shift;    my $class = shift;
8038    my $node = shift;    my $node = shift;
8039    my $s = \$_[0];    #my $s = \$_[0];
8040    my $onerror = $_[1];    my $onerror = $_[1];
8041      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8042    
8043    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8044    
# Line 6310  sub set_inner_html ($$$) { Line 8057  sub set_inner_html ($$$) {
8057      }      }
8058    
8059      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8060      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8061    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8062      ## TODO: If non-html element      ## TODO: If non-html element
8063    
8064      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8065    
8066    ## TODO: Support for $get_wrapper
8067    
8068      ## Step 1 # MUST      ## Step 1 # MUST
8069      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8070      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6325  sub set_inner_html ($$$) { Line 8074  sub set_inner_html ($$$) {
8074    
8075      ## Step 8 # MUST      ## Step 8 # MUST
8076      my $i = 0;      my $i = 0;
8077      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8078      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8079      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8080        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8081        $input = $get_wrapper->($input);
8082        $p->{set_nc} = sub {
8083        my $self = shift;        my $self = shift;
8084    
8085        pop @{$self->{prev_char}};        my $char = '';
8086        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8087            $char = $self->{next_nc};
8088            delete $self->{next_nc};
8089            $self->{nc} = ord $char;
8090          } else {
8091            $self->{char_buffer} = '';
8092            $self->{char_buffer_pos} = 0;
8093            
8094            my $count = $input->manakai_read_until
8095                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8096                 $self->{char_buffer_pos});
8097            if ($count) {
8098              $self->{line_prev} = $self->{line};
8099              $self->{column_prev} = $self->{column};
8100              $self->{column}++;
8101              $self->{nc}
8102                  = ord substr ($self->{char_buffer},
8103                                $self->{char_buffer_pos}++, 1);
8104              return;
8105            }
8106            
8107            if ($input->read ($char, 1)) {
8108              $self->{nc} = ord $char;
8109            } else {
8110              $self->{nc} = -1;
8111              return;
8112            }
8113          }
8114    
8115        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8116        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8117        $column++;  
8118          if ($self->{nc} == 0x000A) { # LF
8119        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8120          $line++;          $p->{column} = 0;
         $column = 0;  
8121          !!!cp ('i1');          !!!cp ('i1');
8122        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8123          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8124          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8125          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8126          $column = 0;            $self->{next_nc} = $next;
8127            }
8128            $self->{nc} = 0x000A; # LF # MUST
8129            $p->{line}++;
8130            $p->{column} = 0;
8131          !!!cp ('i2');          !!!cp ('i2');
8132        } 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  
8133          !!!cp ('i4');          !!!cp ('i4');
8134          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8135          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8136        }        }
8137      };      };
8138      $p->{prev_char} = [-1, -1, -1];  
8139      $p->{next_char} = -1;      $p->{read_until} = sub {
8140              #my ($scalar, $specials_range, $offset) = @_;
8141          return 0 if defined $p->{next_nc};
8142    
8143          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8144          my $offset = $_[2] || 0;
8145          
8146          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8147            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8148            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8149              substr ($_[0], $offset)
8150                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8151              my $count = $+[0] - $-[0];
8152              if ($count) {
8153                $p->{column} += $count;
8154                $p->{char_buffer_pos} += $count;
8155                $p->{line_prev} = $p->{line};
8156                $p->{column_prev} = $p->{column} - 1;
8157                $p->{nc} = -1;
8158              }
8159              return $count;
8160            } else {
8161              return 0;
8162            }
8163          } else {
8164            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8165            if ($count) {
8166              $p->{column} += $count;
8167              $p->{column_prev} += $count;
8168              $p->{nc} = -1;
8169            }
8170            return $count;
8171          }
8172        }; # $p->{read_until}
8173    
8174      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8175        my (%opt) = @_;        my (%opt) = @_;
8176        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8177          my $column = $opt{column};
8178          if (defined $opt{token} and defined $opt{token}->{line}) {
8179            $line = $opt{token}->{line};
8180            $column = $opt{token}->{column};
8181          }
8182          warn "Parse error ($opt{type}) at line $line column $column\n";
8183      };      };
8184      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8185        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8186      };      };
8187            
8188        my $char_onerror = sub {
8189          my (undef, $type, %opt) = @_;
8190          $ponerror->(layer => 'encode',
8191                      line => $p->{line}, column => $p->{column} + 1,
8192                      %opt, type => $type);
8193        }; # $char_onerror
8194        $input->onerror ($char_onerror);
8195    
8196      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8197      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8198    
# Line 6388  sub set_inner_html ($$$) { Line 8214  sub set_inner_html ($$$) {
8214          unless defined $p->{content_model};          unless defined $p->{content_model};
8215          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8216    
8217      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8218          ## TODO: Foreign element OK?
8219    
8220      ## Step 3      ## Step 3
8221      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6398  sub set_inner_html ($$$) { Line 8225  sub set_inner_html ($$$) {
8225      $doc->append_child ($root);      $doc->append_child ($root);
8226    
8227      ## Step 5 # MUST      ## Step 5 # MUST
8228      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8229    
8230      undef $p->{head_element};      undef $p->{head_element};
8231        undef $p->{head_element_inserted};
8232    
8233      ## Step 6 # MUST      ## Step 6 # MUST
8234      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6444  sub set_inner_html ($$$) { Line 8272  sub set_inner_html ($$$) {
8272      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8273    
8274      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8275    
8276        delete $p->{parse_error}; # delete loop
8277    } else {    } else {
8278      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";
8279    }    }

Legend:
Removed from v.1.93  
changed lines
  Added in v.1.204

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24