/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Diff of /markup/html/whatpm/Whatpm/HTML.pm.src

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.100 by wakaba, Sun Mar 9 04:08:41 2008 UTC revision 1.205 by wakaba, Mon Oct 13 06:18:31 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          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709          } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715      $self->{next_char} = -1 and return if $i >= length $$s;      ($self->{line_prev}, $self->{column_prev})
716      $self->{next_char} = ord substr $$s, $i++, 1;          = ($self->{line}, $self->{column});
717      $column++;      $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    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
922        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
923        ## combined with the original insertion mode.  In thie parser,
924        ## they are stored together in the bit-or'ed form.
925    
926  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
927    
# Line 325  sub IN_TABLE_IM () { TABLE_IMS } Line 944  sub IN_TABLE_IM () { TABLE_IMS }
944  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
945  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
946  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
947  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
948    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
949  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
950    
951  ## 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 953  sub IN_COLUMN_GROUP_IM () { 0b10 }
953  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
954    my $self = shift;    my $self = shift;
955    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
956      #$self->{s_kwd}; # state keyword - initialized when used
957      #$self->{entity__value}; # initialized when used
958      #$self->{entity__match}; # initialized when used
959    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
960    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
961    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
962    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
963    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
964    $self->{char} = [];    delete $self->{self_closing};
965    # $self->{next_char}    $self->{char_buffer} = '';
966      $self->{char_buffer_pos} = 0;
967      $self->{nc} = -1; # next input character
968      #$self->{next_nc}
969    !!!next-input-character;    !!!next-input-character;
970    $self->{token} = [];    $self->{token} = [];
971    # $self->{escape}    # $self->{escape}
# Line 350  sub _initialize_tokenizer ($) { Line 976  sub _initialize_tokenizer ($) {
976  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
977  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
978  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
980  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
981  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
982  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
983  ##        ->{name}  ##        ->{name}
984  ##        ->{value}  ##        ->{value}
985  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
986  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
987    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
988    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
989    ##     while the token is pushed back to the stack.
990    
991  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
992    
# Line 367  sub _initialize_tokenizer ($) { Line 996  sub _initialize_tokenizer ($) {
996  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
997  ## and removed from the list.  ## and removed from the list.
998    
999  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1000  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1001  ## contains some requirements that are not detected by the  
1002  ## parsing algorithm:  my $is_space = {
1003  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1004  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1005  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1006  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1007  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1008  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1009    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1010    
1011  sub _get_next_token ($) {  sub _get_next_token ($) {
1012    my $self = shift;    my $self = shift;
1013    
1014      if ($self->{self_closing}) {
1015        !!!parse-error (type => 'nestc', token => $self->{ct});
1016        ## NOTE: The |self_closing| flag is only set by start tag token.
1017        ## In addition, when a start tag token is emitted, it is always set to
1018        ## |ct|.
1019        delete $self->{self_closing};
1020      }
1021    
1022    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1023        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1024      return shift @{$self->{token}};      return shift @{$self->{token}};
1025    }    }
1026    
1027    A: {    A: {
1028      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1029        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1030    
1031          if ($self->{nc} == 0x0026) { # &
1032            !!!cp (0.1);
1033            ## NOTE: In the spec, the tokenizer is switched to the
1034            ## "entity data state".  In this implementation, the tokenizer
1035            ## is switched to the |ENTITY_STATE|, which is an implementation
1036            ## of the "consume a character reference" algorithm.
1037            $self->{entity_add} = -1;
1038            $self->{prev_state} = DATA_STATE;
1039            $self->{state} = ENTITY_STATE;
1040            !!!next-input-character;
1041            redo A;
1042          } elsif ($self->{nc} == 0x003C) { # <
1043            !!!cp (0.2);
1044            $self->{state} = TAG_OPEN_STATE;
1045            !!!next-input-character;
1046            redo A;
1047          } elsif ($self->{nc} == -1) {
1048            !!!cp (0.3);
1049            !!!emit ({type => END_OF_FILE_TOKEN,
1050                      line => $self->{line}, column => $self->{column}});
1051            last A; ## TODO: ok?
1052          } else {
1053            !!!cp (0.4);
1054            #
1055          }
1056    
1057          # Anything else
1058          my $token = {type => CHARACTER_TOKEN,
1059                       data => chr $self->{nc},
1060                       line => $self->{line}, column => $self->{column},
1061                      };
1062          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1063    
1064          ## Stay in the state.
1065          !!!next-input-character;
1066          !!!emit ($token);
1067          redo A;
1068        } elsif ($self->{state} == DATA_STATE) {
1069          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1070          if ($self->{nc} == 0x0026) { # &
1071            $self->{s_kwd} = '';
1072          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1073              not $self->{escape}) {              not $self->{escape}) {
1074            !!!cp (1);            !!!cp (1);
1075            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1076              ## "entity data state".  In this implementation, the tokenizer
1077              ## is switched to the |ENTITY_STATE|, which is an implementation
1078              ## of the "consume a character reference" algorithm.
1079              $self->{entity_add} = -1;
1080              $self->{prev_state} = DATA_STATE;
1081              $self->{state} = ENTITY_STATE;
1082            !!!next-input-character;            !!!next-input-character;
1083            redo A;            redo A;
1084          } else {          } else {
1085            !!!cp (2);            !!!cp (2);
1086            #            #
1087          }          }
1088        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1089          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1090            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1091              if ($self->{prev_char}->[0] == 0x002D and # -            
1092                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1093                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1094                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1095                $self->{escape} = 1;              $self->{s_kwd} = '--';
1096              } else {              #
1097                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1098              }              !!!cp (4);
1099                $self->{s_kwd} = '--';
1100                #
1101            } else {            } else {
1102              !!!cp (5);              !!!cp (5);
1103                #
1104            }            }
1105          }          }
1106                    
1107          #          #
1108        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1109            if (length $self->{s_kwd}) {
1110              !!!cp (5.1);
1111              $self->{s_kwd} .= '!';
1112              #
1113            } else {
1114              !!!cp (5.2);
1115              #$self->{s_kwd} = '';
1116              #
1117            }
1118            #
1119          } elsif ($self->{nc} == 0x003C) { # <
1120          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1121              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1122               not $self->{escape})) {               not $self->{escape})) {
# Line 428  sub _get_next_token ($) { Line 1126  sub _get_next_token ($) {
1126            redo A;            redo A;
1127          } else {          } else {
1128            !!!cp (7);            !!!cp (7);
1129              $self->{s_kwd} = '';
1130            #            #
1131          }          }
1132        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1133          if ($self->{escape} and          if ($self->{escape} and
1134              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1135            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1136              !!!cp (8);              !!!cp (8);
1137              delete $self->{escape};              delete $self->{escape};
1138            } else {            } else {
# Line 444  sub _get_next_token ($) { Line 1142  sub _get_next_token ($) {
1142            !!!cp (10);            !!!cp (10);
1143          }          }
1144                    
1145            $self->{s_kwd} = '';
1146          #          #
1147        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1148          !!!cp (11);          !!!cp (11);
1149          !!!emit ({type => END_OF_FILE_TOKEN});          $self->{s_kwd} = '';
1150            !!!emit ({type => END_OF_FILE_TOKEN,
1151                      line => $self->{line}, column => $self->{column}});
1152          last A; ## TODO: ok?          last A; ## TODO: ok?
1153        } else {        } else {
1154          !!!cp (12);          !!!cp (12);
1155            $self->{s_kwd} = '';
1156            #
1157        }        }
1158    
1159        # Anything else        # Anything else
1160        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1161                     data => chr $self->{next_char}};                     data => chr $self->{nc},
1162        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1163        !!!next-input-character;                    };
1164          if ($self->{read_until}->($token->{data}, q[-!<>&],
1165        !!!emit ($token);                                  length $token->{data})) {
1166            $self->{s_kwd} = '';
1167        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  
1168    
1169        unless (defined $token) {        ## Stay in the data state.
1170          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1171          !!!cp (13);          !!!cp (13);
1172          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
1173        } else {        } else {
1174          !!!cp (14);          !!!cp (14);
1175          !!!emit ($token);          ## Stay in the state.
1176        }        }
1177          !!!next-input-character;
1178          !!!emit ($token);
1179        redo A;        redo A;
1180      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1181        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1182          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1183            !!!cp (15);            !!!cp (15);
1184            !!!next-input-character;            !!!next-input-character;
1185            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1186            redo A;            redo A;
1187            } elsif ($self->{nc} == 0x0021) { # !
1188              !!!cp (15.1);
1189              $self->{s_kwd} = '<' unless $self->{escape};
1190              #
1191          } else {          } else {
1192            !!!cp (16);            !!!cp (16);
1193            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1194          }          }
1195    
1196            ## reconsume
1197            $self->{state} = DATA_STATE;
1198            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1199                      line => $self->{line_prev},
1200                      column => $self->{column_prev},
1201                     });
1202            redo A;
1203        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1204          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1205            !!!cp (17);            !!!cp (17);
1206            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1207            !!!next-input-character;            !!!next-input-character;
1208            redo A;            redo A;
1209          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1210            !!!cp (18);            !!!cp (18);
1211            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1212            !!!next-input-character;            !!!next-input-character;
1213            redo A;            redo A;
1214          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1215                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1216            !!!cp (19);            !!!cp (19);
1217            $self->{current_token}            $self->{ct}
1218              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1219                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1220                   line => $self->{line_prev},
1221                   column => $self->{column_prev}};
1222            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1223            !!!next-input-character;            !!!next-input-character;
1224            redo A;            redo A;
1225          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1226                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1227            !!!cp (20);            !!!cp (20);
1228            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1229                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{nc}),
1230                                        line => $self->{line_prev},
1231                                        column => $self->{column_prev}};
1232            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1233            !!!next-input-character;            !!!next-input-character;
1234            redo A;            redo A;
1235          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1236            !!!cp (21);            !!!cp (21);
1237            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1238                              line => $self->{line_prev},
1239                              column => $self->{column_prev});
1240            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1241            !!!next-input-character;            !!!next-input-character;
1242    
1243            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1244                        line => $self->{line_prev},
1245                        column => $self->{column_prev},
1246                       });
1247    
1248            redo A;            redo A;
1249          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1250            !!!cp (22);            !!!cp (22);
1251            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1252                              line => $self->{line_prev},
1253                              column => $self->{column_prev});
1254            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1255            ## $self->{next_char} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1256                                        line => $self->{line_prev},
1257                                        column => $self->{column_prev},
1258                                       };
1259              ## $self->{nc} is intentionally left as is
1260            redo A;            redo A;
1261          } else {          } else {
1262            !!!cp (23);            !!!cp (23);
1263            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1264                              line => $self->{line_prev},
1265                              column => $self->{column_prev});
1266            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1267            ## reconsume            ## reconsume
1268    
1269            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1270                        line => $self->{line_prev},
1271                        column => $self->{column_prev},
1272                       });
1273    
1274            redo A;            redo A;
1275          }          }
# Line 551  sub _get_next_token ($) { Line 1277  sub _get_next_token ($) {
1277          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1278        }        }
1279      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1280        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1281          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;  
1282    
1283                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1284            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1285                redo A;          if (defined $self->{last_stag_name}) {
1286              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1287            }            $self->{s_kwd} = '';
1288            push @next_char, $self->{next_char};            ## Reconsume.
1289                    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...  
           }  
1290          } else {          } else {
1291            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1292              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1293            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1294            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1295            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1296              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1297                        line => $l, column => $c,
1298                       });
1299            redo A;            redo A;
1300          }          }
1301        }        }
1302          
1303        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1304            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1305          !!!cp (29);          !!!cp (29);
1306          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct}
1307                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1308                   tag_name => chr ($self->{nc} + 0x0020),
1309                   line => $l, column => $c};
1310          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1311          !!!next-input-character;          !!!next-input-character;
1312          redo A;          redo A;
1313        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1314                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1315          !!!cp (30);          !!!cp (30);
1316          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1317                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{nc}),
1318                                      line => $l, column => $c};
1319          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1320          !!!next-input-character;          !!!next-input-character;
1321          redo A;          redo A;
1322        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1323          !!!cp (31);          !!!cp (31);
1324          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1325                            line => $self->{line_prev}, ## "<" in "</>"
1326                            column => $self->{column_prev} - 1);
1327          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1328          !!!next-input-character;          !!!next-input-character;
1329          redo A;          redo A;
1330        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1331          !!!cp (32);          !!!cp (32);
1332          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1333          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1334          # reconsume          # reconsume
1335    
1336          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1337                      line => $l, column => $c,
1338                     });
1339    
1340          redo A;          redo A;
1341        } else {        } else {
1342          !!!cp (33);          !!!cp (33);
1343          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1344          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1345          ## $self->{next_char} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1346          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1347                                      column => $self->{column_prev} - 1,
1348                                     };
1349            ## NOTE: $self->{nc} is intentionally left as is.
1350            ## Although the "anything else" case of the spec not explicitly
1351            ## states that the next input character is to be reconsumed,
1352            ## it will be included to the |data| of the comment token
1353            ## generated from the bogus end tag, as defined in the
1354            ## "bogus comment state" entry.
1355            redo A;
1356          }
1357        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1358          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1359          if (length $ch) {
1360            my $CH = $ch;
1361            $ch =~ tr/a-z/A-Z/;
1362            my $nch = chr $self->{nc};
1363            if ($nch eq $ch or $nch eq $CH) {
1364              !!!cp (24);
1365              ## Stay in the state.
1366              $self->{s_kwd} .= $nch;
1367              !!!next-input-character;
1368              redo A;
1369            } else {
1370              !!!cp (25);
1371              $self->{state} = DATA_STATE;
1372              ## Reconsume.
1373              !!!emit ({type => CHARACTER_TOKEN,
1374                        data => '</' . $self->{s_kwd},
1375                        line => $self->{line_prev},
1376                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1377                       });
1378              redo A;
1379            }
1380          } else { # after "<{tag-name}"
1381            unless ($is_space->{$self->{nc}} or
1382                    {
1383                     0x003E => 1, # >
1384                     0x002F => 1, # /
1385                     -1 => 1, # EOF
1386                    }->{$self->{nc}}) {
1387              !!!cp (26);
1388              ## Reconsume.
1389              $self->{state} = DATA_STATE;
1390              !!!emit ({type => CHARACTER_TOKEN,
1391                        data => '</' . $self->{s_kwd},
1392                        line => $self->{line_prev},
1393                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1394                       });
1395              redo A;
1396            } else {
1397              !!!cp (27);
1398              $self->{ct}
1399                  = {type => END_TAG_TOKEN,
1400                     tag_name => $self->{last_stag_name},
1401                     line => $self->{line_prev},
1402                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1403              $self->{state} = TAG_NAME_STATE;
1404              ## Reconsume.
1405              redo A;
1406            }
1407        }        }
1408      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1409        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  
1410          !!!cp (34);          !!!cp (34);
1411          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1412          !!!next-input-character;          !!!next-input-character;
1413          redo A;          redo A;
1414        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1415          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1416            !!!cp (35);            !!!cp (35);
1417            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1418                = 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) {  
1419            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1420            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1421            #  ## NOTE: This should never be reached.            #  ## NOTE: This should never be reached.
1422            #  !!! cp (36);            #  !!! cp (36);
1423            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 670  sub _get_next_token ($) { Line 1425  sub _get_next_token ($) {
1425              !!!cp (37);              !!!cp (37);
1426            #}            #}
1427          } else {          } else {
1428            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1429          }          }
1430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1431          !!!next-input-character;          !!!next-input-character;
1432    
1433          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1434    
1435          redo A;          redo A;
1436        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1437                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1438          !!!cp (38);          !!!cp (38);
1439          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1440            # start tag or end tag            # start tag or end tag
1441          ## Stay in this state          ## Stay in this state
1442          !!!next-input-character;          !!!next-input-character;
1443          redo A;          redo A;
1444        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1445          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1446          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1447            !!!cp (39);            !!!cp (39);
1448            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1449                = 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) {  
1450            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1451            #if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1452            #  ## NOTE: This state should never be reached.            #  ## NOTE: This state should never be reached.
1453            #  !!! cp (40);            #  !!! cp (40);
1454            #  !!! parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
# Line 703  sub _get_next_token ($) { Line 1456  sub _get_next_token ($) {
1456              !!!cp (41);              !!!cp (41);
1457            #}            #}
1458          } else {          } else {
1459            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1460          }          }
1461          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1462          # reconsume          # reconsume
1463    
1464          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1465    
1466          redo A;          redo A;
1467        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1468            !!!cp (42);
1469            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1470          !!!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  
1471          redo A;          redo A;
1472        } else {        } else {
1473          !!!cp (44);          !!!cp (44);
1474          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1475            # start tag or end tag            # start tag or end tag
1476          ## Stay in the state          ## Stay in the state
1477          !!!next-input-character;          !!!next-input-character;
1478          redo A;          redo A;
1479        }        }
1480      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1481        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  
1482          !!!cp (45);          !!!cp (45);
1483          ## Stay in the state          ## Stay in the state
1484          !!!next-input-character;          !!!next-input-character;
1485          redo A;          redo A;
1486        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1487          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1488            !!!cp (46);            !!!cp (46);
1489            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1490                = 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) {  
1491            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1492            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1493              !!!cp (47);              !!!cp (47);
1494              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1495            } else {            } else {
1496              !!!cp (48);              !!!cp (48);
1497            }            }
1498          } else {          } else {
1499            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1500          }          }
1501          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1502          !!!next-input-character;          !!!next-input-character;
1503    
1504          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1505    
1506          redo A;          redo A;
1507        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1508                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1509          !!!cp (49);          !!!cp (49);
1510          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1511                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1512                   value => '',
1513                   line => $self->{line}, column => $self->{column}};
1514          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1515          !!!next-input-character;          !!!next-input-character;
1516          redo A;          redo A;
1517        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1518            !!!cp (50);
1519            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1520          !!!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  
1521          redo A;          redo A;
1522        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1523          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1524          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1525            !!!cp (52);            !!!cp (52);
1526            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1527                = 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) {  
1528            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1529            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1530              !!!cp (53);              !!!cp (53);
1531              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1532            } else {            } else {
1533              !!!cp (54);              !!!cp (54);
1534            }            }
1535          } else {          } else {
1536            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1537          }          }
1538          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1539          # reconsume          # reconsume
1540    
1541          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1542    
1543          redo A;          redo A;
1544        } else {        } else {
# Line 819  sub _get_next_token ($) { Line 1546  sub _get_next_token ($) {
1546               0x0022 => 1, # "               0x0022 => 1, # "
1547               0x0027 => 1, # '               0x0027 => 1, # '
1548               0x003D => 1, # =               0x003D => 1, # =
1549              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1550            !!!cp (55);            !!!cp (55);
1551            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1552          } else {          } else {
1553            !!!cp (56);            !!!cp (56);
1554          }          }
1555          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1556                                value => ''};              = {name => chr ($self->{nc}),
1557                   value => '',
1558                   line => $self->{line}, column => $self->{column}};
1559          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1560          !!!next-input-character;          !!!next-input-character;
1561          redo A;          redo A;
1562        }        }
1563      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1564        my $before_leave = sub {        my $before_leave = sub {
1565          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1566              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1567            !!!cp (57);            !!!cp (57);
1568            !!!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});
1569            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1570          } else {          } else {
1571            !!!cp (58);            !!!cp (58);
1572            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1573              = $self->{current_attribute};              = $self->{ca};
1574          }          }
1575        }; # $before_leave        }; # $before_leave
1576    
1577        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  
1578          !!!cp (59);          !!!cp (59);
1579          $before_leave->();          $before_leave->();
1580          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1581          !!!next-input-character;          !!!next-input-character;
1582          redo A;          redo A;
1583        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1584          !!!cp (60);          !!!cp (60);
1585          $before_leave->();          $before_leave->();
1586          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1587          !!!next-input-character;          !!!next-input-character;
1588          redo A;          redo A;
1589        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1590          $before_leave->();          $before_leave->();
1591          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1592            !!!cp (61);            !!!cp (61);
1593            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1594                = 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) {  
1595            !!!cp (62);            !!!cp (62);
1596            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1597            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1598              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1599            }            }
1600          } else {          } else {
1601            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1602          }          }
1603          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1604          !!!next-input-character;          !!!next-input-character;
1605    
1606          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1607    
1608          redo A;          redo A;
1609        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1610                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1611          !!!cp (63);          !!!cp (63);
1612          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1613          ## Stay in the state          ## Stay in the state
1614          !!!next-input-character;          !!!next-input-character;
1615          redo A;          redo A;
1616        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1617            !!!cp (64);
1618          $before_leave->();          $before_leave->();
1619            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1620          !!!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  
1621          redo A;          redo A;
1622        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1623          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1624          $before_leave->();          $before_leave->();
1625          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1626            !!!cp (66);            !!!cp (66);
1627            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1628                = 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) {  
1629            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1630            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1631              !!!cp (67);              !!!cp (67);
1632              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1633            } else {            } else {
# Line 924  sub _get_next_token ($) { Line 1635  sub _get_next_token ($) {
1635              !!!cp (68);              !!!cp (68);
1636            }            }
1637          } else {          } else {
1638            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1639          }          }
1640          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1641          # reconsume          # reconsume
1642    
1643          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1644    
1645          redo A;          redo A;
1646        } else {        } else {
1647          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1648              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1649            !!!cp (69);            !!!cp (69);
1650            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1651          } else {          } else {
1652            !!!cp (70);            !!!cp (70);
1653          }          }
1654          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1655          ## Stay in the state          ## Stay in the state
1656          !!!next-input-character;          !!!next-input-character;
1657          redo A;          redo A;
1658        }        }
1659      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1660        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  
1661          !!!cp (71);          !!!cp (71);
1662          ## Stay in the state          ## Stay in the state
1663          !!!next-input-character;          !!!next-input-character;
1664          redo A;          redo A;
1665        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1666          !!!cp (72);          !!!cp (72);
1667          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1668          !!!next-input-character;          !!!next-input-character;
1669          redo A;          redo A;
1670        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1671          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1672            !!!cp (73);            !!!cp (73);
1673            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1674                = 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) {  
1675            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1676            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1677              !!!cp (74);              !!!cp (74);
1678              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1679            } else {            } else {
# Line 976  sub _get_next_token ($) { Line 1681  sub _get_next_token ($) {
1681              !!!cp (75);              !!!cp (75);
1682            }            }
1683          } else {          } else {
1684            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1685          }          }
1686          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1687          !!!next-input-character;          !!!next-input-character;
1688    
1689          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1690    
1691          redo A;          redo A;
1692        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1693                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1694          !!!cp (76);          !!!cp (76);
1695          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1696                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1697                   value => '',
1698                   line => $self->{line}, column => $self->{column}};
1699          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1700          !!!next-input-character;          !!!next-input-character;
1701          redo A;          redo A;
1702        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1703            !!!cp (77);
1704            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1705          !!!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  
1706          redo A;          redo A;
1707        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1708          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1709          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1710            !!!cp (79);            !!!cp (79);
1711            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1712                = 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) {  
1713            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1714            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1715              !!!cp (80);              !!!cp (80);
1716              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1717            } else {            } else {
# Line 1025  sub _get_next_token ($) { Line 1719  sub _get_next_token ($) {
1719              !!!cp (81);              !!!cp (81);
1720            }            }
1721          } else {          } else {
1722            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1723          }          }
1724          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1725          # reconsume          # reconsume
1726    
1727          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1728    
1729          redo A;          redo A;
1730        } else {        } else {
1731          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1732          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1733                                value => ''};            !!!cp (78);
1734              !!!parse-error (type => 'bad attribute name');
1735            } else {
1736              !!!cp (82);
1737            }
1738            $self->{ca}
1739                = {name => chr ($self->{nc}),
1740                   value => '',
1741                   line => $self->{line}, column => $self->{column}};
1742          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1743          !!!next-input-character;          !!!next-input-character;
1744          redo A;                  redo A;        
1745        }        }
1746      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1747        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        
1748          !!!cp (83);          !!!cp (83);
1749          ## Stay in the state          ## Stay in the state
1750          !!!next-input-character;          !!!next-input-character;
1751          redo A;          redo A;
1752        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1753          !!!cp (84);          !!!cp (84);
1754          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1755          !!!next-input-character;          !!!next-input-character;
1756          redo A;          redo A;
1757        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1758          !!!cp (85);          !!!cp (85);
1759          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1760          ## reconsume          ## reconsume
1761          redo A;          redo A;
1762        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1763          !!!cp (86);          !!!cp (86);
1764          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1765          !!!next-input-character;          !!!next-input-character;
1766          redo A;          redo A;
1767        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1768          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1769            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1770            !!!cp (87);            !!!cp (87);
1771            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1772                = 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) {  
1773            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1774            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1775              !!!cp (88);              !!!cp (88);
1776              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1777            } else {            } else {
# Line 1082  sub _get_next_token ($) { Line 1779  sub _get_next_token ($) {
1779              !!!cp (89);              !!!cp (89);
1780            }            }
1781          } else {          } else {
1782            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1783          }          }
1784          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1785          !!!next-input-character;          !!!next-input-character;
1786    
1787          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1788    
1789          redo A;          redo A;
1790        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1791          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1792          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1793            !!!cp (90);            !!!cp (90);
1794            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1795                = 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) {  
1796            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1797            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1798              !!!cp (91);              !!!cp (91);
1799              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1800            } else {            } else {
# Line 1107  sub _get_next_token ($) { Line 1802  sub _get_next_token ($) {
1802              !!!cp (92);              !!!cp (92);
1803            }            }
1804          } else {          } else {
1805            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1806          }          }
1807          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1808          ## reconsume          ## reconsume
1809    
1810          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1811    
1812          redo A;          redo A;
1813        } else {        } else {
1814          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1815            !!!cp (93);            !!!cp (93);
1816            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1817          } else {          } else {
1818            !!!cp (94);            !!!cp (94);
1819          }          }
1820          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1821          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1822          !!!next-input-character;          !!!next-input-character;
1823          redo A;          redo A;
1824        }        }
1825      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1826        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1827          !!!cp (95);          !!!cp (95);
1828          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1829          !!!next-input-character;          !!!next-input-character;
1830          redo A;          redo A;
1831        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1832          !!!cp (96);          !!!cp (96);
1833          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1834          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1835            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1836            ## implementation of the "consume a character reference" algorithm.
1837            $self->{prev_state} = $self->{state};
1838            $self->{entity_add} = 0x0022; # "
1839            $self->{state} = ENTITY_STATE;
1840          !!!next-input-character;          !!!next-input-character;
1841          redo A;          redo A;
1842        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1843          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1844          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1845            !!!cp (97);            !!!cp (97);
1846            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1847                = 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) {  
1848            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1849            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1850              !!!cp (98);              !!!cp (98);
1851              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1852            } else {            } else {
# Line 1156  sub _get_next_token ($) { Line 1854  sub _get_next_token ($) {
1854              !!!cp (99);              !!!cp (99);
1855            }            }
1856          } else {          } else {
1857            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1858          }          }
1859          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1860          ## reconsume          ## reconsume
1861    
1862          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1863    
1864          redo A;          redo A;
1865        } else {        } else {
1866          !!!cp (100);          !!!cp (100);
1867          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1868            $self->{read_until}->($self->{ca}->{value},
1869                                  q["&],
1870                                  length $self->{ca}->{value});
1871    
1872          ## Stay in the state          ## Stay in the state
1873          !!!next-input-character;          !!!next-input-character;
1874          redo A;          redo A;
1875        }        }
1876      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1877        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1878          !!!cp (101);          !!!cp (101);
1879          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1880          !!!next-input-character;          !!!next-input-character;
1881          redo A;          redo A;
1882        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1883          !!!cp (102);          !!!cp (102);
1884          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1885          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1886            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1887            ## implementation of the "consume a character reference" algorithm.
1888            $self->{entity_add} = 0x0027; # '
1889            $self->{prev_state} = $self->{state};
1890            $self->{state} = ENTITY_STATE;
1891          !!!next-input-character;          !!!next-input-character;
1892          redo A;          redo A;
1893        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1894          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1895          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1896            !!!cp (103);            !!!cp (103);
1897            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1898                = 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) {  
1899            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1900            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1901              !!!cp (104);              !!!cp (104);
1902              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1903            } else {            } else {
# Line 1200  sub _get_next_token ($) { Line 1905  sub _get_next_token ($) {
1905              !!!cp (105);              !!!cp (105);
1906            }            }
1907          } else {          } else {
1908            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1909          }          }
1910          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1911          ## reconsume          ## reconsume
1912    
1913          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1914    
1915          redo A;          redo A;
1916        } else {        } else {
1917          !!!cp (106);          !!!cp (106);
1918          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1919            $self->{read_until}->($self->{ca}->{value},
1920                                  q['&],
1921                                  length $self->{ca}->{value});
1922    
1923          ## Stay in the state          ## Stay in the state
1924          !!!next-input-character;          !!!next-input-character;
1925          redo A;          redo A;
1926        }        }
1927      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1928        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  
1929          !!!cp (107);          !!!cp (107);
1930          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1931          !!!next-input-character;          !!!next-input-character;
1932          redo A;          redo A;
1933        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1934          !!!cp (108);          !!!cp (108);
1935          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1936          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1937            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1938            ## implementation of the "consume a character reference" algorithm.
1939            $self->{entity_add} = -1;
1940            $self->{prev_state} = $self->{state};
1941            $self->{state} = ENTITY_STATE;
1942          !!!next-input-character;          !!!next-input-character;
1943          redo A;          redo A;
1944        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1945          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1946            !!!cp (109);            !!!cp (109);
1947            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1948                = 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) {  
1949            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1950            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1951              !!!cp (110);              !!!cp (110);
1952              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1953            } else {            } else {
# Line 1247  sub _get_next_token ($) { Line 1955  sub _get_next_token ($) {
1955              !!!cp (111);              !!!cp (111);
1956            }            }
1957          } else {          } else {
1958            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1959          }          }
1960          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1961          !!!next-input-character;          !!!next-input-character;
1962    
1963          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1964    
1965          redo A;          redo A;
1966        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1967          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1968          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1969            !!!cp (112);            !!!cp (112);
1970            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1971                = 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) {  
1972            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1973            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1974              !!!cp (113);              !!!cp (113);
1975              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1976            } else {            } else {
# Line 1272  sub _get_next_token ($) { Line 1978  sub _get_next_token ($) {
1978              !!!cp (114);              !!!cp (114);
1979            }            }
1980          } else {          } else {
1981            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1982          }          }
1983          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1984          ## reconsume          ## reconsume
1985    
1986          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1987    
1988          redo A;          redo A;
1989        } else {        } else {
# Line 1285  sub _get_next_token ($) { Line 1991  sub _get_next_token ($) {
1991               0x0022 => 1, # "               0x0022 => 1, # "
1992               0x0027 => 1, # '               0x0027 => 1, # '
1993               0x003D => 1, # =               0x003D => 1, # =
1994              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1995            !!!cp (115);            !!!cp (115);
1996            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1997          } else {          } else {
1998            !!!cp (116);            !!!cp (116);
1999          }          }
2000          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
2001            $self->{read_until}->($self->{ca}->{value},
2002                                  q["'=& >],
2003                                  length $self->{ca}->{value});
2004    
2005          ## Stay in the state          ## Stay in the state
2006          !!!next-input-character;          !!!next-input-character;
2007          redo A;          redo A;
2008        }        }
     } 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;  
2009      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2010        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  
2011          !!!cp (118);          !!!cp (118);
2012          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2013          !!!next-input-character;          !!!next-input-character;
2014          redo A;          redo A;
2015        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2016          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2017            !!!cp (119);            !!!cp (119);
2018            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2019                = 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) {  
2020            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2021            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2022              !!!cp (120);              !!!cp (120);
2023              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2024            } else {            } else {
# Line 1344  sub _get_next_token ($) { Line 2026  sub _get_next_token ($) {
2026              !!!cp (121);              !!!cp (121);
2027            }            }
2028          } else {          } else {
2029            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2030          }          }
2031          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2032          !!!next-input-character;          !!!next-input-character;
2033    
2034          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2035    
2036          redo A;          redo A;
2037        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2038            !!!cp (122);
2039            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2040          !!!next-input-character;          !!!next-input-character;
2041          if ($self->{next_char} == 0x003E and # >          redo A;
2042              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2043              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2044            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2045            !!!cp (122);            !!!cp (122.3);
2046            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2047            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2048              if ($self->{ct}->{attributes}) {
2049                !!!cp (122.1);
2050                !!!parse-error (type => 'end tag attribute');
2051              } else {
2052                ## NOTE: This state should never be reached.
2053                !!!cp (122.2);
2054              }
2055          } else {          } else {
2056            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2057          }          }
2058          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2059          # next-input-character is already done          ## Reconsume.
2060            !!!emit ($self->{ct}); # start tag or end tag
2061          redo A;          redo A;
2062        } else {        } else {
2063          !!!cp (124);          !!!cp ('124.1');
2064          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2065          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2066          ## reconsume          ## reconsume
2067          redo A;          redo A;
2068        }        }
2069      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2070        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2071                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2072        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2073              !!!parse-error (type => 'nestc', token => $self->{ct});
2074        BC: {            ## TODO: Different type than slash in start tag
2075          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2076            !!!cp (124);            if ($self->{ct}->{attributes}) {
2077            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2078            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2079              } else {
2080            !!!emit ($token);              !!!cp ('124.5');
2081              }
2082              ## TODO: Test |<title></title/>|
2083            } else {
2084              !!!cp ('124.3');
2085              $self->{self_closing} = 1;
2086            }
2087    
2088            redo A;          $self->{state} = DATA_STATE;
2089          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2090    
2091            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2092    
2093            redo A;          redo A;
2094          } elsif ($self->{nc} == -1) {
2095            !!!parse-error (type => 'unclosed tag');
2096            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2097              !!!cp (124.7);
2098              $self->{last_stag_name} = $self->{ct}->{tag_name};
2099            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2100              if ($self->{ct}->{attributes}) {
2101                !!!cp (124.5);
2102                !!!parse-error (type => 'end tag attribute');
2103              } else {
2104                ## NOTE: This state should never be reached.
2105                !!!cp (124.6);
2106              }
2107          } else {          } else {
2108            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $token->{data} .= chr ($self->{next_char});  
           !!!next-input-character;  
           redo BC;  
2109          }          }
2110        } # BC          $self->{state} = DATA_STATE;
2111            ## Reconsume.
2112        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2113      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2114          } else {
2115            !!!cp ('124.4');
2116            !!!parse-error (type => 'nestc');
2117            ## TODO: This error type is wrong.
2118            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2119            ## Reconsume.
2120            redo A;
2121          }
2122        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2123        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2124    
2125        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2126        push @next_char, $self->{next_char};        ## consumes characters one-by-one basis.
2127                
2128        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2129            !!!cp (124);
2130            $self->{state} = DATA_STATE;
2131          !!!next-input-character;          !!!next-input-character;
2132          push @next_char, $self->{next_char};  
2133          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2134            !!!cp (127);          redo A;
2135            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};        } elsif ($self->{nc} == -1) {
2136            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2137            !!!next-input-character;          $self->{state} = DATA_STATE;
2138            redo A;          ## reconsume
2139          } else {  
2140            !!!cp (128);          !!!emit ($self->{ct}); # comment
2141          }          redo A;
2142        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2143                 $self->{next_char} == 0x0064) { # d          !!!cp (126);
2144            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2145            $self->{read_until}->($self->{ct}->{data},
2146                                  q[>],
2147                                  length $self->{ct}->{data});
2148    
2149            ## Stay in the state.
2150          !!!next-input-character;          !!!next-input-character;
2151          push @next_char, $self->{next_char};          redo A;
2152          if ($self->{next_char} == 0x004F or # O        }
2153              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2154            !!!next-input-character;        ## (only happen if PCDATA state)
2155            push @next_char, $self->{next_char};        
2156            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2157                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2158              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2159              push @next_char, $self->{next_char};          !!!next-input-character;
2160              if ($self->{next_char} == 0x0054 or # T          redo A;
2161                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2162                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2163                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2164                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2165                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2166                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2167                  push @next_char, $self->{next_char};          !!!next-input-character;
2168                  if ($self->{next_char} == 0x0050 or # P          redo A;
2169                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2170                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2171                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2172                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2173                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2174                      !!!cp (129);          $self->{s_kwd} = '[';
2175                      ## TODO: What a stupid code this is!          !!!next-input-character;
2176                      $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);  
         }  
2177        } else {        } else {
2178          !!!cp (136);          !!!cp (136);
2179        }        }
2180    
2181        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2182        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2183        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2184          ## Reconsume.
2185        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2186          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2187                                    line => $self->{line_prev},
2188                                    column => $self->{column_prev} - 1,
2189                                   };
2190        redo A;        redo A;
2191              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2192        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2193        ## 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);
2194            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2195                                      line => $self->{line_prev},
2196                                      column => $self->{column_prev} - 2,
2197                                     };
2198            $self->{state} = COMMENT_START_STATE;
2199            !!!next-input-character;
2200            redo A;
2201          } else {
2202            !!!cp (128);
2203            !!!parse-error (type => 'bogus comment',
2204                            line => $self->{line_prev},
2205                            column => $self->{column_prev} - 2);
2206            $self->{state} = BOGUS_COMMENT_STATE;
2207            ## Reconsume.
2208            $self->{ct} = {type => COMMENT_TOKEN,
2209                                      data => '-',
2210                                      line => $self->{line_prev},
2211                                      column => $self->{column_prev} - 2,
2212                                     };
2213            redo A;
2214          }
2215        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2216          ## ASCII case-insensitive.
2217          if ($self->{nc} == [
2218                undef,
2219                0x004F, # O
2220                0x0043, # C
2221                0x0054, # T
2222                0x0059, # Y
2223                0x0050, # P
2224              ]->[length $self->{s_kwd}] or
2225              $self->{nc} == [
2226                undef,
2227                0x006F, # o
2228                0x0063, # c
2229                0x0074, # t
2230                0x0079, # y
2231                0x0070, # p
2232              ]->[length $self->{s_kwd}]) {
2233            !!!cp (131);
2234            ## Stay in the state.
2235            $self->{s_kwd} .= chr $self->{nc};
2236            !!!next-input-character;
2237            redo A;
2238          } elsif ((length $self->{s_kwd}) == 6 and
2239                   ($self->{nc} == 0x0045 or # E
2240                    $self->{nc} == 0x0065)) { # e
2241            !!!cp (129);
2242            $self->{state} = DOCTYPE_STATE;
2243            $self->{ct} = {type => DOCTYPE_TOKEN,
2244                                      quirks => 1,
2245                                      line => $self->{line_prev},
2246                                      column => $self->{column_prev} - 7,
2247                                     };
2248            !!!next-input-character;
2249            redo A;
2250          } else {
2251            !!!cp (132);        
2252            !!!parse-error (type => 'bogus comment',
2253                            line => $self->{line_prev},
2254                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2255            $self->{state} = BOGUS_COMMENT_STATE;
2256            ## Reconsume.
2257            $self->{ct} = {type => COMMENT_TOKEN,
2258                                      data => $self->{s_kwd},
2259                                      line => $self->{line_prev},
2260                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2261                                     };
2262            redo A;
2263          }
2264        } elsif ($self->{state} == MD_CDATA_STATE) {
2265          if ($self->{nc} == {
2266                '[' => 0x0043, # C
2267                '[C' => 0x0044, # D
2268                '[CD' => 0x0041, # A
2269                '[CDA' => 0x0054, # T
2270                '[CDAT' => 0x0041, # A
2271              }->{$self->{s_kwd}}) {
2272            !!!cp (135.1);
2273            ## Stay in the state.
2274            $self->{s_kwd} .= chr $self->{nc};
2275            !!!next-input-character;
2276            redo A;
2277          } elsif ($self->{s_kwd} eq '[CDATA' and
2278                   $self->{nc} == 0x005B) { # [
2279            !!!cp (135.2);
2280            $self->{ct} = {type => CHARACTER_TOKEN,
2281                                      data => '',
2282                                      line => $self->{line_prev},
2283                                      column => $self->{column_prev} - 7};
2284            $self->{state} = CDATA_SECTION_STATE;
2285            !!!next-input-character;
2286            redo A;
2287          } else {
2288            !!!cp (135.3);
2289            !!!parse-error (type => 'bogus comment',
2290                            line => $self->{line_prev},
2291                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2292            $self->{state} = BOGUS_COMMENT_STATE;
2293            ## Reconsume.
2294            $self->{ct} = {type => COMMENT_TOKEN,
2295                                      data => $self->{s_kwd},
2296                                      line => $self->{line_prev},
2297                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2298                                     };
2299            redo A;
2300          }
2301      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2302        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2303          !!!cp (137);          !!!cp (137);
2304          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2305          !!!next-input-character;          !!!next-input-character;
2306          redo A;          redo A;
2307        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2308          !!!cp (138);          !!!cp (138);
2309          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2310          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2311          !!!next-input-character;          !!!next-input-character;
2312    
2313          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2314    
2315          redo A;          redo A;
2316        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2317          !!!cp (139);          !!!cp (139);
2318          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2319          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2320          ## reconsume          ## reconsume
2321    
2322          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2323    
2324          redo A;          redo A;
2325        } else {        } else {
2326          !!!cp (140);          !!!cp (140);
2327          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2328              .= chr ($self->{next_char});              .= chr ($self->{nc});
2329          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2330          !!!next-input-character;          !!!next-input-character;
2331          redo A;          redo A;
2332        }        }
2333      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2334        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2335          !!!cp (141);          !!!cp (141);
2336          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2337          !!!next-input-character;          !!!next-input-character;
2338          redo A;          redo A;
2339        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2340          !!!cp (142);          !!!cp (142);
2341          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2342          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2343          !!!next-input-character;          !!!next-input-character;
2344    
2345          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2346    
2347          redo A;          redo A;
2348        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2349          !!!cp (143);          !!!cp (143);
2350          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2351          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2352          ## reconsume          ## reconsume
2353    
2354          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2355    
2356          redo A;          redo A;
2357        } else {        } else {
2358          !!!cp (144);          !!!cp (144);
2359          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2360              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2361          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2362          !!!next-input-character;          !!!next-input-character;
2363          redo A;          redo A;
2364        }        }
2365      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2366        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2367          !!!cp (145);          !!!cp (145);
2368          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2369          !!!next-input-character;          !!!next-input-character;
2370          redo A;          redo A;
2371        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2372          !!!cp (146);          !!!cp (146);
2373          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2374          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2375          ## reconsume          ## reconsume
2376    
2377          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2378    
2379          redo A;          redo A;
2380        } else {        } else {
2381          !!!cp (147);          !!!cp (147);
2382          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2383            $self->{read_until}->($self->{ct}->{data},
2384                                  q[-],
2385                                  length $self->{ct}->{data});
2386    
2387          ## Stay in the state          ## Stay in the state
2388          !!!next-input-character;          !!!next-input-character;
2389          redo A;          redo A;
2390        }        }
2391      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2392        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2393          !!!cp (148);          !!!cp (148);
2394          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2395          !!!next-input-character;          !!!next-input-character;
2396          redo A;          redo A;
2397        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2398          !!!cp (149);          !!!cp (149);
2399          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2400          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2401          ## reconsume          ## reconsume
2402    
2403          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2404    
2405          redo A;          redo A;
2406        } else {        } else {
2407          !!!cp (150);          !!!cp (150);
2408          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2409          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2410          !!!next-input-character;          !!!next-input-character;
2411          redo A;          redo A;
2412        }        }
2413      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2414        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2415          !!!cp (151);          !!!cp (151);
2416          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2417          !!!next-input-character;          !!!next-input-character;
2418    
2419          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2420    
2421          redo A;          redo A;
2422        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2423          !!!cp (152);          !!!cp (152);
2424          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2425          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2426                            column => $self->{column_prev});
2427            $self->{ct}->{data} .= '-'; # comment
2428          ## Stay in the state          ## Stay in the state
2429          !!!next-input-character;          !!!next-input-character;
2430          redo A;          redo A;
2431        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2432          !!!cp (153);          !!!cp (153);
2433          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2434          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2435          ## reconsume          ## reconsume
2436    
2437          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2438    
2439          redo A;          redo A;
2440        } else {        } else {
2441          !!!cp (154);          !!!cp (154);
2442          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2443          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2444                            column => $self->{column_prev});
2445            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2446          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2447          !!!next-input-character;          !!!next-input-character;
2448          redo A;          redo A;
2449        }        }
2450      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2451        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  
2452          !!!cp (155);          !!!cp (155);
2453          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2454          !!!next-input-character;          !!!next-input-character;
# Line 1643  sub _get_next_token ($) { Line 2461  sub _get_next_token ($) {
2461          redo A;          redo A;
2462        }        }
2463      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2464        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  
2465          !!!cp (157);          !!!cp (157);
2466          ## Stay in the state          ## Stay in the state
2467          !!!next-input-character;          !!!next-input-character;
2468          redo A;          redo A;
2469        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2470          !!!cp (158);          !!!cp (158);
2471          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2472          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2473          !!!next-input-character;          !!!next-input-character;
2474    
2475          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2476    
2477          redo A;          redo A;
2478        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2479          !!!cp (159);          !!!cp (159);
2480          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2481          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2482          ## reconsume          ## reconsume
2483    
2484          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2485    
2486          redo A;          redo A;
2487        } else {        } else {
2488          !!!cp (160);          !!!cp (160);
2489          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2490              = {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  
2491          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2492          !!!next-input-character;          !!!next-input-character;
2493          redo A;          redo A;
2494        }        }
2495      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2496  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2497        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  
2498          !!!cp (161);          !!!cp (161);
2499          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2500          !!!next-input-character;          !!!next-input-character;
2501          redo A;          redo A;
2502        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2503          !!!cp (162);          !!!cp (162);
2504          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2505          !!!next-input-character;          !!!next-input-character;
2506    
2507          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2508    
2509          redo A;          redo A;
2510        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2511          !!!cp (163);          !!!cp (163);
2512          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2513          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2514          ## reconsume          ## reconsume
2515    
2516          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2517          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2518    
2519          redo A;          redo A;
2520        } else {        } else {
2521          !!!cp (164);          !!!cp (164);
2522          $self->{current_token}->{name}          $self->{ct}->{name}
2523            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2524          ## Stay in the state          ## Stay in the state
2525          !!!next-input-character;          !!!next-input-character;
2526          redo A;          redo A;
2527        }        }
2528      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2529        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  
2530          !!!cp (165);          !!!cp (165);
2531          ## Stay in the state          ## Stay in the state
2532          !!!next-input-character;          !!!next-input-character;
2533          redo A;          redo A;
2534        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2535          !!!cp (166);          !!!cp (166);
2536          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2537          !!!next-input-character;          !!!next-input-character;
2538    
2539          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2540    
2541          redo A;          redo A;
2542        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2543          !!!cp (167);          !!!cp (167);
2544          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2545          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2546          ## reconsume          ## reconsume
2547    
2548          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2549          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2550    
2551          redo A;          redo A;
2552        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2553                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2554            $self->{state} = PUBLIC_STATE;
2555            $self->{s_kwd} = chr $self->{nc};
2556          !!!next-input-character;          !!!next-input-character;
2557          if ($self->{next_char} == 0x0055 or # U          redo A;
2558              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2559            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2560            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2561                $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  
2562          !!!next-input-character;          !!!next-input-character;
2563          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);  
         }  
   
         #  
2564        } else {        } else {
2565          !!!cp (180);          !!!cp (180);
2566            !!!parse-error (type => 'string after DOCTYPE name');
2567            $self->{ct}->{quirks} = 1;
2568    
2569            $self->{state} = BOGUS_DOCTYPE_STATE;
2570          !!!next-input-character;          !!!next-input-character;
2571          #          redo A;
2572        }        }
2573        } elsif ($self->{state} == PUBLIC_STATE) {
2574          ## ASCII case-insensitive
2575          if ($self->{nc} == [
2576                undef,
2577                0x0055, # U
2578                0x0042, # B
2579                0x004C, # L
2580                0x0049, # I
2581              ]->[length $self->{s_kwd}] or
2582              $self->{nc} == [
2583                undef,
2584                0x0075, # u
2585                0x0062, # b
2586                0x006C, # l
2587                0x0069, # i
2588              ]->[length $self->{s_kwd}]) {
2589            !!!cp (175);
2590            ## Stay in the state.
2591            $self->{s_kwd} .= chr $self->{nc};
2592            !!!next-input-character;
2593            redo A;
2594          } elsif ((length $self->{s_kwd}) == 5 and
2595                   ($self->{nc} == 0x0043 or # C
2596                    $self->{nc} == 0x0063)) { # c
2597            !!!cp (168);
2598            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2599            !!!next-input-character;
2600            redo A;
2601          } else {
2602            !!!cp (169);
2603            !!!parse-error (type => 'string after DOCTYPE name',
2604                            line => $self->{line_prev},
2605                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2606            $self->{ct}->{quirks} = 1;
2607    
2608        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2609        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2610            redo A;
2611          }
2612        } elsif ($self->{state} == SYSTEM_STATE) {
2613          ## ASCII case-insensitive
2614          if ($self->{nc} == [
2615                undef,
2616                0x0059, # Y
2617                0x0053, # S
2618                0x0054, # T
2619                0x0045, # E
2620              ]->[length $self->{s_kwd}] or
2621              $self->{nc} == [
2622                undef,
2623                0x0079, # y
2624                0x0073, # s
2625                0x0074, # t
2626                0x0065, # e
2627              ]->[length $self->{s_kwd}]) {
2628            !!!cp (170);
2629            ## Stay in the state.
2630            $self->{s_kwd} .= chr $self->{nc};
2631            !!!next-input-character;
2632            redo A;
2633          } elsif ((length $self->{s_kwd}) == 5 and
2634                   ($self->{nc} == 0x004D or # M
2635                    $self->{nc} == 0x006D)) { # m
2636            !!!cp (171);
2637            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2638            !!!next-input-character;
2639            redo A;
2640          } else {
2641            !!!cp (172);
2642            !!!parse-error (type => 'string after DOCTYPE name',
2643                            line => $self->{line_prev},
2644                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2645            $self->{ct}->{quirks} = 1;
2646    
2647        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2648        # next-input-character is already done          ## Reconsume.
2649        redo A;          redo A;
2650          }
2651      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2652        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}}) {  
2653          !!!cp (181);          !!!cp (181);
2654          ## Stay in the state          ## Stay in the state
2655          !!!next-input-character;          !!!next-input-character;
2656          redo A;          redo A;
2657        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2658          !!!cp (182);          !!!cp (182);
2659          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2660          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2661          !!!next-input-character;          !!!next-input-character;
2662          redo A;          redo A;
2663        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2664          !!!cp (183);          !!!cp (183);
2665          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2666          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2667          !!!next-input-character;          !!!next-input-character;
2668          redo A;          redo A;
2669        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2670          !!!cp (184);          !!!cp (184);
2671          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2672    
2673          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2674          !!!next-input-character;          !!!next-input-character;
2675    
2676          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2677          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2678    
2679          redo A;          redo A;
2680        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2681          !!!cp (185);          !!!cp (185);
2682          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2683    
2684          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2685          ## reconsume          ## reconsume
2686    
2687          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2688          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2689    
2690          redo A;          redo A;
2691        } else {        } else {
2692          !!!cp (186);          !!!cp (186);
2693          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2694          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2695    
2696          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2697          !!!next-input-character;          !!!next-input-character;
2698          redo A;          redo A;
2699        }        }
2700      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2701        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2702          !!!cp (187);          !!!cp (187);
2703          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2704          !!!next-input-character;          !!!next-input-character;
2705          redo A;          redo A;
2706        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2707          !!!cp (188);          !!!cp (188);
2708          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2709    
2710          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2711          !!!next-input-character;          !!!next-input-character;
2712    
2713          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2714          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2715    
2716          redo A;          redo A;
2717        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2718          !!!cp (189);          !!!cp (189);
2719          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2720    
2721          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2722          ## reconsume          ## reconsume
2723    
2724          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2725          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2726    
2727          redo A;          redo A;
2728        } else {        } else {
2729          !!!cp (190);          !!!cp (190);
2730          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2731              .= chr $self->{next_char};              .= chr $self->{nc};
2732            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2733                                  length $self->{ct}->{pubid});
2734    
2735          ## Stay in the state          ## Stay in the state
2736          !!!next-input-character;          !!!next-input-character;
2737          redo A;          redo A;
2738        }        }
2739      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2740        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2741          !!!cp (191);          !!!cp (191);
2742          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2743          !!!next-input-character;          !!!next-input-character;
2744          redo A;          redo A;
2745        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2746          !!!cp (192);          !!!cp (192);
2747          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2748    
2749          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2750          !!!next-input-character;          !!!next-input-character;
2751    
2752          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2753          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2754    
2755          redo A;          redo A;
2756        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2757          !!!cp (193);          !!!cp (193);
2758          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2759    
2760          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2761          ## reconsume          ## reconsume
2762    
2763          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2764          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2765    
2766          redo A;          redo A;
2767        } else {        } else {
2768          !!!cp (194);          !!!cp (194);
2769          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2770              .= chr $self->{next_char};              .= chr $self->{nc};
2771            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2772                                  length $self->{ct}->{pubid});
2773    
2774          ## Stay in the state          ## Stay in the state
2775          !!!next-input-character;          !!!next-input-character;
2776          redo A;          redo A;
2777        }        }
2778      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2779        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}}) {  
2780          !!!cp (195);          !!!cp (195);
2781          ## Stay in the state          ## Stay in the state
2782          !!!next-input-character;          !!!next-input-character;
2783          redo A;          redo A;
2784        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2785          !!!cp (196);          !!!cp (196);
2786          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2787          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2788          !!!next-input-character;          !!!next-input-character;
2789          redo A;          redo A;
2790        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2791          !!!cp (197);          !!!cp (197);
2792          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2793          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2794          !!!next-input-character;          !!!next-input-character;
2795          redo A;          redo A;
2796        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2797          !!!cp (198);          !!!cp (198);
2798          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2799          !!!next-input-character;          !!!next-input-character;
2800    
2801          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2802    
2803          redo A;          redo A;
2804        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2805          !!!cp (199);          !!!cp (199);
2806          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2807    
2808          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2809          ## reconsume          ## reconsume
2810    
2811          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2812          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2813    
2814          redo A;          redo A;
2815        } else {        } else {
2816          !!!cp (200);          !!!cp (200);
2817          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2818          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2819    
2820          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2821          !!!next-input-character;          !!!next-input-character;
2822          redo A;          redo A;
2823        }        }
2824      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2825        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}}) {  
2826          !!!cp (201);          !!!cp (201);
2827          ## Stay in the state          ## Stay in the state
2828          !!!next-input-character;          !!!next-input-character;
2829          redo A;          redo A;
2830        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2831          !!!cp (202);          !!!cp (202);
2832          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2833          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2834          !!!next-input-character;          !!!next-input-character;
2835          redo A;          redo A;
2836        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2837          !!!cp (203);          !!!cp (203);
2838          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2839          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2840          !!!next-input-character;          !!!next-input-character;
2841          redo A;          redo A;
2842        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2843          !!!cp (204);          !!!cp (204);
2844          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2845          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2846          !!!next-input-character;          !!!next-input-character;
2847    
2848          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2849          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2850    
2851          redo A;          redo A;
2852        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2853          !!!cp (205);          !!!cp (205);
2854          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2855    
2856          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2857          ## reconsume          ## reconsume
2858    
2859          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2860          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2861    
2862          redo A;          redo A;
2863        } else {        } else {
2864          !!!cp (206);          !!!cp (206);
2865          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2866          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2867    
2868          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2869          !!!next-input-character;          !!!next-input-character;
2870          redo A;          redo A;
2871        }        }
2872      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2873        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2874          !!!cp (207);          !!!cp (207);
2875          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2876          !!!next-input-character;          !!!next-input-character;
2877          redo A;          redo A;
2878        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2879          !!!cp (208);          !!!cp (208);
2880          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2881    
2882          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2883          !!!next-input-character;          !!!next-input-character;
2884    
2885          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2886          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2887    
2888          redo A;          redo A;
2889        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2890          !!!cp (209);          !!!cp (209);
2891          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2892    
2893          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2894          ## reconsume          ## reconsume
2895    
2896          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2897          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2898    
2899          redo A;          redo A;
2900        } else {        } else {
2901          !!!cp (210);          !!!cp (210);
2902          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2903              .= chr $self->{next_char};              .= chr $self->{nc};
2904            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2905                                  length $self->{ct}->{sysid});
2906    
2907          ## Stay in the state          ## Stay in the state
2908          !!!next-input-character;          !!!next-input-character;
2909          redo A;          redo A;
2910        }        }
2911      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2912        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2913          !!!cp (211);          !!!cp (211);
2914          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2915          !!!next-input-character;          !!!next-input-character;
2916          redo A;          redo A;
2917        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2918          !!!cp (212);          !!!cp (212);
2919          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2920    
2921          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2922          !!!next-input-character;          !!!next-input-character;
2923    
2924          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2925          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2926    
2927          redo A;          redo A;
2928        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2929          !!!cp (213);          !!!cp (213);
2930          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2931    
2932          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2933          ## reconsume          ## reconsume
2934    
2935          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2936          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2937    
2938          redo A;          redo A;
2939        } else {        } else {
2940          !!!cp (214);          !!!cp (214);
2941          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2942              .= chr $self->{next_char};              .= chr $self->{nc};
2943            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2944                                  length $self->{ct}->{sysid});
2945    
2946          ## Stay in the state          ## Stay in the state
2947          !!!next-input-character;          !!!next-input-character;
2948          redo A;          redo A;
2949        }        }
2950      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2951        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}}) {  
2952          !!!cp (215);          !!!cp (215);
2953          ## Stay in the state          ## Stay in the state
2954          !!!next-input-character;          !!!next-input-character;
2955          redo A;          redo A;
2956        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2957          !!!cp (216);          !!!cp (216);
2958          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2959          !!!next-input-character;          !!!next-input-character;
2960    
2961          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2962    
2963          redo A;          redo A;
2964        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2965          !!!cp (217);          !!!cp (217);
2966          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2967          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2968          ## reconsume          ## reconsume
2969    
2970          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2971          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2972    
2973          redo A;          redo A;
2974        } else {        } else {
2975          !!!cp (218);          !!!cp (218);
2976          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2977          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2978    
2979          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2980          !!!next-input-character;          !!!next-input-character;
2981          redo A;          redo A;
2982        }        }
2983      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2984        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2985          !!!cp (219);          !!!cp (219);
2986          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2987          !!!next-input-character;          !!!next-input-character;
2988    
2989          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2990    
2991          redo A;          redo A;
2992        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2993          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2994          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2995          ## reconsume          ## reconsume
2996    
2997          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2998    
2999          redo A;          redo A;
3000        } else {        } else {
3001          !!!cp (221);          !!!cp (221);
3002            my $s = '';
3003            $self->{read_until}->($s, q[>], 0);
3004    
3005          ## Stay in the state          ## Stay in the state
3006          !!!next-input-character;          !!!next-input-character;
3007          redo A;          redo A;
3008        }        }
3009      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3010        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3011      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3012    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3013          
3014    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3015  } # _get_next_token          !!!cp (221.1);
3016            $self->{state} = CDATA_SECTION_MSE1_STATE;
3017            !!!next-input-character;
3018            redo A;
3019          } elsif ($self->{nc} == -1) {
3020            $self->{state} = DATA_STATE;
3021            !!!next-input-character;
3022            if (length $self->{ct}->{data}) { # character
3023              !!!cp (221.2);
3024              !!!emit ($self->{ct}); # character
3025            } else {
3026              !!!cp (221.3);
3027              ## No token to emit. $self->{ct} is discarded.
3028            }        
3029            redo A;
3030          } else {
3031            !!!cp (221.4);
3032            $self->{ct}->{data} .= chr $self->{nc};
3033            $self->{read_until}->($self->{ct}->{data},
3034                                  q<]>,
3035                                  length $self->{ct}->{data});
3036    
3037  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3038    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3039            redo A;
3040          }
3041    
3042    if ({        ## ISSUE: "text tokens" in spec.
3043         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3044         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3045         $additional => 1,          !!!cp (221.5);
3046        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3047      !!!cp (1001);          !!!next-input-character;
3048      ## Don't consume          redo A;
3049      ## No error        } else {
3050      return undef;          !!!cp (221.6);
3051    } elsif ($self->{next_char} == 0x0023) { # #          $self->{ct}->{data} .= ']';
3052      !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3053      if ($self->{next_char} == 0x0078 or # x          ## Reconsume.
3054          $self->{next_char} == 0x0058) { # X          redo A;
3055        my $code;        }
3056        X: {      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3057          my $x_char = $self->{next_char};        if ($self->{nc} == 0x003E) { # >
3058          !!!next-input-character;          $self->{state} = DATA_STATE;
3059          if (0x0030 <= $self->{next_char} and          !!!next-input-character;
3060              $self->{next_char} <= 0x0039) { # 0..9          if (length $self->{ct}->{data}) { # character
3061            !!!cp (1002);            !!!cp (221.7);
3062            $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;  
3063          } else {          } else {
3064            !!!cp (1007);            !!!cp (221.8);
3065            !!!parse-error (type => 'no refc');            ## No token to emit. $self->{ct} is discarded.
3066          }          }
3067            redo A;
3068          } elsif ($self->{nc} == 0x005D) { # ]
3069            !!!cp (221.9); # character
3070            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3071            ## Stay in the state.
3072            !!!next-input-character;
3073            redo A;
3074          } else {
3075            !!!cp (221.11);
3076            $self->{ct}->{data} .= ']]'; # character
3077            $self->{state} = CDATA_SECTION_STATE;
3078            ## Reconsume.
3079            redo A;
3080          }
3081        } elsif ($self->{state} == ENTITY_STATE) {
3082          if ($is_space->{$self->{nc}} or
3083              {
3084                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3085                $self->{entity_add} => 1,
3086              }->{$self->{nc}}) {
3087            !!!cp (1001);
3088            ## Don't consume
3089            ## No error
3090            ## Return nothing.
3091            #
3092          } elsif ($self->{nc} == 0x0023) { # #
3093            !!!cp (999);
3094            $self->{state} = ENTITY_HASH_STATE;
3095            $self->{s_kwd} = '#';
3096            !!!next-input-character;
3097            redo A;
3098          } elsif ((0x0041 <= $self->{nc} and
3099                    $self->{nc} <= 0x005A) or # A..Z
3100                   (0x0061 <= $self->{nc} and
3101                    $self->{nc} <= 0x007A)) { # a..z
3102            !!!cp (998);
3103            require Whatpm::_NamedEntityList;
3104            $self->{state} = ENTITY_NAME_STATE;
3105            $self->{s_kwd} = chr $self->{nc};
3106            $self->{entity__value} = $self->{s_kwd};
3107            $self->{entity__match} = 0;
3108            !!!next-input-character;
3109            redo A;
3110          } else {
3111            !!!cp (1027);
3112            !!!parse-error (type => 'bare ero');
3113            ## Return nothing.
3114            #
3115          }
3116    
3117          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        ## NOTE: No character is consumed by the "consume a character
3118            !!!cp (1008);        ## reference" algorithm.  In other word, there is an "&" character
3119            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        ## that does not introduce a character reference, which would be
3120            $code = 0xFFFD;        ## appended to the parent element or the attribute value in later
3121          } elsif ($code > 0x10FFFF) {        ## process of the tokenizer.
3122            !!!cp (1009);  
3123            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);        if ($self->{prev_state} == DATA_STATE) {
3124            $code = 0xFFFD;          !!!cp (997);
3125          } elsif ($code == 0x000D) {          $self->{state} = $self->{prev_state};
3126            !!!cp (1010);          ## Reconsume.
3127            !!!parse-error (type => 'CR character reference');          !!!emit ({type => CHARACTER_TOKEN, data => '&',
3128            $code = 0x000A;                    line => $self->{line_prev},
3129          } elsif (0x80 <= $code and $code <= 0x9F) {                    column => $self->{column_prev},
3130            !!!cp (1011);                   });
3131            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          redo A;
3132            $code = $c1_entity_char->{$code};        } else {
3133          }          !!!cp (996);
3134            $self->{ca}->{value} .= '&';
3135          return {type => CHARACTER_TOKEN, data => chr $code,          $self->{state} = $self->{prev_state};
3136                  has_reference => 1};          ## Reconsume.
3137        } # X          redo A;
3138      } elsif (0x0030 <= $self->{next_char} and        }
3139               $self->{next_char} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_HASH_STATE) {
3140        my $code = $self->{next_char} - 0x0030;        if ($self->{nc} == 0x0078 or # x
3141        !!!next-input-character;            $self->{nc} == 0x0058) { # X
3142                  !!!cp (995);
3143        while (0x0030 <= $self->{next_char} and          $self->{state} = HEXREF_X_STATE;
3144                  $self->{next_char} <= 0x0039) { # 0..9          $self->{s_kwd} .= chr $self->{nc};
3145            !!!next-input-character;
3146            redo A;
3147          } elsif (0x0030 <= $self->{nc} and
3148                   $self->{nc} <= 0x0039) { # 0..9
3149            !!!cp (994);
3150            $self->{state} = NCR_NUM_STATE;
3151            $self->{s_kwd} = $self->{nc} - 0x0030;
3152            !!!next-input-character;
3153            redo A;
3154          } else {
3155            !!!parse-error (type => 'bare nero',
3156                            line => $self->{line_prev},
3157                            column => $self->{column_prev} - 1);
3158    
3159            ## NOTE: According to the spec algorithm, nothing is returned,
3160            ## and then "&#" is appended to the parent element or the attribute
3161            ## value in the later processing.
3162    
3163            if ($self->{prev_state} == DATA_STATE) {
3164              !!!cp (1019);
3165              $self->{state} = $self->{prev_state};
3166              ## Reconsume.
3167              !!!emit ({type => CHARACTER_TOKEN,
3168                        data => '&#',
3169                        line => $self->{line_prev},
3170                        column => $self->{column_prev} - 1,
3171                       });
3172              redo A;
3173            } else {
3174              !!!cp (993);
3175              $self->{ca}->{value} .= '&#';
3176              $self->{state} = $self->{prev_state};
3177              ## Reconsume.
3178              redo A;
3179            }
3180          }
3181        } elsif ($self->{state} == NCR_NUM_STATE) {
3182          if (0x0030 <= $self->{nc} and
3183              $self->{nc} <= 0x0039) { # 0..9
3184          !!!cp (1012);          !!!cp (1012);
3185          $code *= 10;          $self->{s_kwd} *= 10;
3186          $code += $self->{next_char} - 0x0030;          $self->{s_kwd} += $self->{nc} - 0x0030;
3187                    
3188            ## Stay in the state.
3189          !!!next-input-character;          !!!next-input-character;
3190        }          redo A;
3191          } elsif ($self->{nc} == 0x003B) { # ;
       if ($self->{next_char} == 0x003B) { # ;  
3192          !!!cp (1013);          !!!cp (1013);
3193          !!!next-input-character;          !!!next-input-character;
3194            #
3195        } else {        } else {
3196          !!!cp (1014);          !!!cp (1014);
3197          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3198            ## Reconsume.
3199            #
3200        }        }
3201    
3202        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3203          my $l = $self->{line_prev};
3204          my $c = $self->{column_prev};
3205          if ($charref_map->{$code}) {
3206          !!!cp (1015);          !!!cp (1015);
3207          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3208          $code = 0xFFFD;                          text => (sprintf 'U+%04X', $code),
3209                            line => $l, column => $c);
3210            $code = $charref_map->{$code};
3211        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3212          !!!cp (1016);          !!!cp (1016);
3213          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!parse-error (type => 'invalid character reference',
3214                            text => (sprintf 'U-%08X', $code),
3215                            line => $l, column => $c);
3216          $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};  
3217        }        }
3218          
3219        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3220      } else {          !!!cp (992);
3221        !!!cp (1019);          $self->{state} = $self->{prev_state};
3222        !!!parse-error (type => 'bare nero');          ## Reconsume.
3223        !!!back-next-input-character ($self->{next_char});          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3224        $self->{next_char} = 0x0023; # #                    line => $l, column => $c,
3225        return undef;                   });
3226      }          redo A;
3227    } elsif ((0x0041 <= $self->{next_char} and        } else {
3228              $self->{next_char} <= 0x005A) or          !!!cp (991);
3229             (0x0061 <= $self->{next_char} and          $self->{ca}->{value} .= chr $code;
3230              $self->{next_char} <= 0x007A)) {          $self->{ca}->{has_reference} = 1;
3231      my $entity_name = chr $self->{next_char};          $self->{state} = $self->{prev_state};
3232      !!!next-input-character;          ## Reconsume.
3233            redo A;
3234      my $value = $entity_name;        }
3235      my $match = 0;      } elsif ($self->{state} == HEXREF_X_STATE) {
3236      require Whatpm::_NamedEntityList;        if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3237      our $EntityChar;            (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3238              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3239      while (length $entity_name < 10 and          # 0..9, A..F, a..f
3240             ## NOTE: Some number greater than the maximum length of entity name          !!!cp (990);
3241             ((0x0041 <= $self->{next_char} and # a          $self->{state} = HEXREF_HEX_STATE;
3242               $self->{next_char} <= 0x005A) or # x          $self->{s_kwd} = 0;
3243              (0x0061 <= $self->{next_char} and # a          ## Reconsume.
3244               $self->{next_char} <= 0x007A) or # z          redo A;
3245              (0x0030 <= $self->{next_char} and # 0        } else {
3246               $self->{next_char} <= 0x0039) or # 9          !!!parse-error (type => 'bare hcro',
3247              $self->{next_char} == 0x003B)) { # ;                          line => $self->{line_prev},
3248        $entity_name .= chr $self->{next_char};                          column => $self->{column_prev} - 2);
3249        if (defined $EntityChar->{$entity_name}) {  
3250          if ($self->{next_char} == 0x003B) { # ;          ## NOTE: According to the spec algorithm, nothing is returned,
3251            !!!cp (1020);          ## and then "&#" followed by "X" or "x" is appended to the parent
3252            $value = $EntityChar->{$entity_name};          ## element or the attribute value in the later processing.
3253            $match = 1;  
3254            !!!next-input-character;          if ($self->{prev_state} == DATA_STATE) {
3255            last;            !!!cp (1005);
3256              $self->{state} = $self->{prev_state};
3257              ## Reconsume.
3258              !!!emit ({type => CHARACTER_TOKEN,
3259                        data => '&' . $self->{s_kwd},
3260                        line => $self->{line_prev},
3261                        column => $self->{column_prev} - length $self->{s_kwd},
3262                       });
3263              redo A;
3264          } else {          } else {
3265            !!!cp (1021);            !!!cp (989);
3266            $value = $EntityChar->{$entity_name};            $self->{ca}->{value} .= '&' . $self->{s_kwd};
3267            $match = -1;            $self->{state} = $self->{prev_state};
3268            !!!next-input-character;            ## Reconsume.
3269              redo A;
3270          }          }
3271        } else {        }
3272          !!!cp (1022);      } elsif ($self->{state} == HEXREF_HEX_STATE) {
3273          $value .= chr $self->{next_char};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3274          $match *= 2;          # 0..9
3275            !!!cp (1002);
3276            $self->{s_kwd} *= 0x10;
3277            $self->{s_kwd} += $self->{nc} - 0x0030;
3278            ## Stay in the state.
3279            !!!next-input-character;
3280            redo A;
3281          } elsif (0x0061 <= $self->{nc} and
3282                   $self->{nc} <= 0x0066) { # a..f
3283            !!!cp (1003);
3284            $self->{s_kwd} *= 0x10;
3285            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3286            ## Stay in the state.
3287            !!!next-input-character;
3288            redo A;
3289          } elsif (0x0041 <= $self->{nc} and
3290                   $self->{nc} <= 0x0046) { # A..F
3291            !!!cp (1004);
3292            $self->{s_kwd} *= 0x10;
3293            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3294            ## Stay in the state.
3295            !!!next-input-character;
3296            redo A;
3297          } elsif ($self->{nc} == 0x003B) { # ;
3298            !!!cp (1006);
3299          !!!next-input-character;          !!!next-input-character;
3300            #
3301          } else {
3302            !!!cp (1007);
3303            !!!parse-error (type => 'no refc',
3304                            line => $self->{line},
3305                            column => $self->{column});
3306            ## Reconsume.
3307            #
3308        }        }
3309      }  
3310              my $code = $self->{s_kwd};
3311      if ($match > 0) {        my $l = $self->{line_prev};
3312        !!!cp (1023);        my $c = $self->{column_prev};
3313        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($charref_map->{$code}) {
3314      } elsif ($match < 0) {          !!!cp (1008);
3315        !!!parse-error (type => 'no refc');          !!!parse-error (type => 'invalid character reference',
3316        if ($in_attr and $match < -1) {                          text => (sprintf 'U+%04X', $code),
3317          !!!cp (1024);                          line => $l, column => $c);
3318          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          $code = $charref_map->{$code};
3319          } elsif ($code > 0x10FFFF) {
3320            !!!cp (1009);
3321            !!!parse-error (type => 'invalid character reference',
3322                            text => (sprintf 'U-%08X', $code),
3323                            line => $l, column => $c);
3324            $code = 0xFFFD;
3325          }
3326    
3327          if ($self->{prev_state} == DATA_STATE) {
3328            !!!cp (988);
3329            $self->{state} = $self->{prev_state};
3330            ## Reconsume.
3331            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3332                      line => $l, column => $c,
3333                     });
3334            redo A;
3335          } else {
3336            !!!cp (987);
3337            $self->{ca}->{value} .= chr $code;
3338            $self->{ca}->{has_reference} = 1;
3339            $self->{state} = $self->{prev_state};
3340            ## Reconsume.
3341            redo A;
3342          }
3343        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3344          if (length $self->{s_kwd} < 30 and
3345              ## NOTE: Some number greater than the maximum length of entity name
3346              ((0x0041 <= $self->{nc} and # a
3347                $self->{nc} <= 0x005A) or # x
3348               (0x0061 <= $self->{nc} and # a
3349                $self->{nc} <= 0x007A) or # z
3350               (0x0030 <= $self->{nc} and # 0
3351                $self->{nc} <= 0x0039) or # 9
3352               $self->{nc} == 0x003B)) { # ;
3353            our $EntityChar;
3354            $self->{s_kwd} .= chr $self->{nc};
3355            if (defined $EntityChar->{$self->{s_kwd}}) {
3356              if ($self->{nc} == 0x003B) { # ;
3357                !!!cp (1020);
3358                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3359                $self->{entity__match} = 1;
3360                !!!next-input-character;
3361                #
3362              } else {
3363                !!!cp (1021);
3364                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3365                $self->{entity__match} = -1;
3366                ## Stay in the state.
3367                !!!next-input-character;
3368                redo A;
3369              }
3370            } else {
3371              !!!cp (1022);
3372              $self->{entity__value} .= chr $self->{nc};
3373              $self->{entity__match} *= 2;
3374              ## Stay in the state.
3375              !!!next-input-character;
3376              redo A;
3377            }
3378          }
3379    
3380          my $data;
3381          my $has_ref;
3382          if ($self->{entity__match} > 0) {
3383            !!!cp (1023);
3384            $data = $self->{entity__value};
3385            $has_ref = 1;
3386            #
3387          } elsif ($self->{entity__match} < 0) {
3388            !!!parse-error (type => 'no refc');
3389            if ($self->{prev_state} != DATA_STATE and # in attribute
3390                $self->{entity__match} < -1) {
3391              !!!cp (1024);
3392              $data = '&' . $self->{s_kwd};
3393              #
3394            } else {
3395              !!!cp (1025);
3396              $data = $self->{entity__value};
3397              $has_ref = 1;
3398              #
3399            }
3400        } else {        } else {
3401          !!!cp (1025);          !!!cp (1026);
3402          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!parse-error (type => 'bare ero',
3403                            line => $self->{line_prev},
3404                            column => $self->{column_prev} - length $self->{s_kwd});
3405            $data = '&' . $self->{s_kwd};
3406            #
3407          }
3408      
3409          ## NOTE: In these cases, when a character reference is found,
3410          ## it is consumed and a character token is returned, or, otherwise,
3411          ## nothing is consumed and returned, according to the spec algorithm.
3412          ## In this implementation, anything that has been examined by the
3413          ## tokenizer is appended to the parent element or the attribute value
3414          ## as string, either literal string when no character reference or
3415          ## entity-replaced string otherwise, in this stage, since any characters
3416          ## that would not be consumed are appended in the data state or in an
3417          ## appropriate attribute value state anyway.
3418    
3419          if ($self->{prev_state} == DATA_STATE) {
3420            !!!cp (986);
3421            $self->{state} = $self->{prev_state};
3422            ## Reconsume.
3423            !!!emit ({type => CHARACTER_TOKEN,
3424                      data => $data,
3425                      line => $self->{line_prev},
3426                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3427                     });
3428            redo A;
3429          } else {
3430            !!!cp (985);
3431            $self->{ca}->{value} .= $data;
3432            $self->{ca}->{has_reference} = 1 if $has_ref;
3433            $self->{state} = $self->{prev_state};
3434            ## Reconsume.
3435            redo A;
3436        }        }
3437      } else {      } else {
3438        !!!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};  
3439      }      }
3440    } else {    } # A  
3441      !!!cp (1027);  
3442      ## no characters are consumed    die "$0: _get_next_token: unexpected case";
3443      !!!parse-error (type => 'bare ero');  } # _get_next_token
     return undef;  
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3444    
3445  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3446    my $self = shift;    my $self = shift;
# Line 2400  sub _initialize_tree_constructor ($) { Line 3449  sub _initialize_tree_constructor ($) {
3449    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3450    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3451    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3452      $self->{document}->set_user_data (manakai_source_line => 1);
3453      $self->{document}->set_user_data (manakai_source_column => 1);
3454  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3455    
3456  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2419  sub _construct_tree ($) { Line 3470  sub _construct_tree ($) {
3470    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3471    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3472    ## 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  
3473        
3474    !!!next-token;    !!!next-token;
3475    
3476    undef $self->{form_element};    undef $self->{form_element};
3477    undef $self->{head_element};    undef $self->{head_element};
3478      undef $self->{head_element_inserted};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
# Line 2454  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
           defined $token->{system_identifier}) {  
3508          !!!cp ('t1');          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          !!!cp ('t2');          !!!cp ('t2');
3512          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!parse-error (type => 'not HTML5', token => $token);
3513          !!!parse-error (type => 'not HTML5');        } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521        } else {        } else {
3522          !!!cp ('t3');          !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
# Line 2481  sub _tree_construction_initial ($) { Line 3536  sub _tree_construction_initial ($) {
3536        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537          !!!cp ('t4');          !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3604            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3605            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3606            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3607            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
           "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3610            !!!cp ('t5');            !!!cp ('t5');
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615              !!!cp ('t6');              !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618              !!!cp ('t7');              !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623            !!!cp ('t8');            !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625          } else {          } else {
# Line 2579  sub _tree_construction_initial ($) { Line 3628  sub _tree_construction_initial ($) {
3628        } else {        } else {
3629          !!!cp ('t10');          !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635            ## TODO: Check the spec: PUBLIC "(limited quirks)" "(quirks)"            ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638            !!!cp ('t11');            !!!cp ('t11');
3639          } else {          } else {
# Line 2602  sub _tree_construction_initial ($) { Line 3652  sub _tree_construction_initial ($) {
3652                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!cp ('t14');        !!!cp ('t14');
3655        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
# Line 2623  sub _tree_construction_initial ($) { Line 3674  sub _tree_construction_initial ($) {
3674          !!!cp ('t17');          !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the "before html" insertion mode.        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
# Line 2652  sub _tree_construction_root_element ($) Line 3703  sub _tree_construction_root_element ($)
3703    B: {    B: {
3704        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!cp ('t19');          !!!cp ('t19');
3706          !!!parse-error (type => 'in html:#DOCTYPE');          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the insertion mode.          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
# Line 2665  sub _tree_construction_root_element ($) Line 3716  sub _tree_construction_root_element ($)
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
# Line 2686  sub _tree_construction_root_element ($) Line 3737  sub _tree_construction_root_element ($)
3737        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3738          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
3739            my $root_element;            my $root_element;
3740            !!!create-element ($root_element, $token->{tag_name}, $token->{attributes});            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741            $self->{document}->append_child ($root_element);            $self->{document}->append_child ($root_element);
3742            push @{$self->{open_elements}}, [$root_element, 'html'];            push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745            if ($token->{attributes}->{manifest}) {            if ($token->{attributes}->{manifest}) {
3746              !!!cp ('t24');              !!!cp ('t24');
3747              $self->{application_cache_selection}              $self->{application_cache_selection}
3748                  ->($token->{attributes}->{manifest}->{value});                  ->($token->{attributes}->{manifest}->{value});
3749              ## ISSUE: No relative reference resolution?              ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753            } else {            } else {
3754              !!!cp ('t25');              !!!cp ('t25');
3755              $self->{application_cache_selection}->(undef);              $self->{application_cache_selection}->(undef);
3756            }            }
3757    
3758              !!!nack ('t25c');
3759    
3760            !!!next-token;            !!!next-token;
3761            return; ## Go to the "before head" insertion mode.            return; ## Go to the "before head" insertion mode.
3762          } else {          } else {
# Line 2716  sub _tree_construction_root_element ($) Line 3773  sub _tree_construction_root_element ($)
3773          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775    
3776      my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3777        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778      $self->{document}->append_child ($root_element);      $self->{document}->append_child ($root_element);
3779      push @{$self->{open_elements}}, [$root_element, 'html'];      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780    
3781      $self->{application_cache_selection}->(undef);      $self->{application_cache_selection}->(undef);
3782    
3783      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
3786    } # B    } # B
3787    
3788    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 2746  sub _reset_insertion_mode ($) { Line 3803  sub _reset_insertion_mode ($) {
3803        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804          $last = 1;          $last = 1;
3805          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3806            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3807                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3808              !!!cp ('t27');          } else {
3809              #            die "_reset_insertion_mode: t27";
           } else {  
             !!!cp ('t28');  
             $node = $self->{inner_html_node};  
           }  
3810          }          }
3811        }        }
3812              
3813        ## Step 4..13        ## Step 4..14
3814        my $new_mode = {        my $new_mode;
3815          if ($node->[1] & FOREIGN_EL) {
3816            !!!cp ('t28.1');
3817            ## NOTE: Strictly spaking, the line below only applies to MathML and
3818            ## SVG elements.  Currently the HTML syntax supports only MathML and
3819            ## SVG elements as foreigners.
3820            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821          } elsif ($node->[1] & TABLE_CELL_EL) {
3822            if ($last) {
3823              !!!cp ('t28.2');
3824              #
3825            } else {
3826              !!!cp ('t28.3');
3827              $new_mode = IN_CELL_IM;
3828            }
3829          } else {
3830            !!!cp ('t28.4');
3831            $new_mode = {
3832                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3833                        ## NOTE: |option| and |optgroup| do not set                        ## NOTE: |option| and |optgroup| do not set
3834                        ## insertion mode to "in select" by themselves.                        ## insertion mode to "in select" by themselves.
                       td => IN_CELL_IM,  
                       th => IN_CELL_IM,  
3835                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3836                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3837                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2774  sub _reset_insertion_mode ($) { Line 3842  sub _reset_insertion_mode ($) {
3842                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3843                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3844                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3845                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3846          }
3847        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848                
3849        ## Step 14        ## Step 15
3850        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3851          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3852            !!!cp ('t29');            !!!cp ('t29');
3853            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 2792  sub _reset_insertion_mode ($) { Line 3861  sub _reset_insertion_mode ($) {
3861          !!!cp ('t31');          !!!cp ('t31');
3862        }        }
3863                
3864        ## Step 15        ## Step 16
3865        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866                
3867        ## Step 16        ## Step 17
3868        $i--;        $i--;
3869        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3870                
3871        ## Step 17        ## Step 18
3872        redo S3;        redo S3;
3873      } # S3      } # S3
3874    
# Line 2910  sub _tree_construction_main ($) { Line 3979  sub _tree_construction_main ($) {
3979    
3980      ## Step 1      ## Step 1
3981      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3982      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3983    
3984      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
3985      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3986      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3987    
3988      ## Step 4      ## Step 3, 4
3989      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
3990    
3991      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($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});  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
3992      !!!next-token;      !!!next-token;
3993    }; # $parse_rcdata    }; # $parse_rcdata
3994    
3995    my $script_start_tag = sub () {    my $script_start_tag = sub () {
3996        ## Step 1
3997      my $script_el;      my $script_el;
3998      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
3999    
4000        ## Step 2
4001      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4002    
4003        ## Step 3
4004        ## TODO: Mark as "already executed", if ...
4005    
4006        ## Step 4
4007        $insert->($script_el);
4008    
4009        ## ISSUE: $script_el is not put into the stack
4010        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4011    
4012        ## Step 5
4013      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4014      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
4015    
4016      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4017          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
4018    
4019        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
4020      !!!next-token;      !!!next-token;
4021    }; # $script_start_tag    }; # $script_start_tag
4022    
4023      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4024      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4025      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4026      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4027    
4028    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4029      my $tag_name = shift;      my $end_tag_token = shift;
4030        my $tag_name = $end_tag_token->{tag_name};
4031    
4032        ## NOTE: The adoption agency algorithm (AAA).
4033    
4034      FET: {      FET: {
4035        ## Step 1        ## Step 1
4036        my $formatting_element;        my $formatting_element;
4037        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4038        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4039          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4040              !!!cp ('t52');
4041              last AFE;
4042            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4043                         eq $tag_name) {
4044            !!!cp ('t51');            !!!cp ('t51');
4045            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4046            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4047            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           !!!cp ('t52');  
           last AFE;  
4048          }          }
4049        } # AFE        } # AFE
4050        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4051          !!!cp ('t53');          !!!cp ('t53');
4052          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4053          ## Ignore the token          ## Ignore the token
4054          !!!next-token;          !!!next-token;
4055          return;          return;
# Line 3047  sub _tree_construction_main ($) { Line 4066  sub _tree_construction_main ($) {
4066              last INSCOPE;              last INSCOPE;
4067            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4068              !!!cp ('t55');              !!!cp ('t55');
4069              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
4070                                text => $token->{tag_name},
4071                                token => $end_tag_token);
4072              ## Ignore the token              ## Ignore the token
4073              !!!next-token;              !!!next-token;
4074              return;              return;
4075            }            }
4076          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
                   table => 1, caption => 1, td => 1, th => 1,  
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4077            !!!cp ('t56');            !!!cp ('t56');
4078            $in_scope = 0;            $in_scope = 0;
4079          }          }
4080        } # INSCOPE        } # INSCOPE
4081        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4082          !!!cp ('t57');          !!!cp ('t57');
4083          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!parse-error (type => 'unmatched end tag',
4084                            text => $token->{tag_name},
4085                            token => $end_tag_token);
4086          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4087          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4088          return;          return;
4089        }        }
4090        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4091          !!!cp ('t58');          !!!cp ('t58');
4092          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!parse-error (type => 'not closed',
4093                            text => $self->{open_elements}->[-1]->[0]
4094                                ->manakai_local_name,
4095                            token => $end_tag_token);
4096        }        }
4097                
4098        ## Step 2        ## Step 2
# Line 3077  sub _tree_construction_main ($) { Line 4100  sub _tree_construction_main ($) {
4100        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4101        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4102          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4103          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4104              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4105              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4106               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4107            !!!cp ('t59');            !!!cp ('t59');
4108            $furthest_block = $node;            $furthest_block = $node;
4109            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4110              ## NOTE: The topmost (eldest) node.
4111          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4112            !!!cp ('t60');            !!!cp ('t60');
4113            last OE;            last OE;
# Line 3166  sub _tree_construction_main ($) { Line 4190  sub _tree_construction_main ($) {
4190        } # S7          } # S7  
4191                
4192        ## Step 8        ## Step 8
4193        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4194            my $foster_parent_element;
4195            my $next_sibling;
4196            OE: for (reverse 0..$#{$self->{open_elements}}) {
4197              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4198                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4199                                 if (defined $parent and $parent->node_type == 1) {
4200                                   !!!cp ('t65.1');
4201                                   $foster_parent_element = $parent;
4202                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4203                                 } else {
4204                                   !!!cp ('t65.2');
4205                                   $foster_parent_element
4206                                     = $self->{open_elements}->[$_ - 1]->[0];
4207                                 }
4208                                 last OE;
4209                               }
4210                             } # OE
4211                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4212                               unless defined $foster_parent_element;
4213            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4214            $open_tables->[-1]->[1] = 1; # tainted
4215          } else {
4216            !!!cp ('t65.3');
4217            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4218          }
4219                
4220        ## Step 9        ## Step 9
4221        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3205  sub _tree_construction_main ($) { Line 4254  sub _tree_construction_main ($) {
4254            $i = $_;            $i = $_;
4255          }          }
4256        } # OE        } # OE
4257        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4258                
4259        ## Step 14        ## Step 14
4260        redo FET;        redo FET;
4261      } # FET      } # FET
4262    }; # $formatting_end_tag    }; # $formatting_end_tag
4263    
   ## NOTE: $open_tables->[-1]->[0] is the "current table".  
   ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.  
   my $open_tables = [[$self->{open_elements}->[0]->[0]]];  
   
4264    $insert = my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4265      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4266    }; # $insert_to_current    }; # $insert_to_current
4267    
4268    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4269      my $child = shift;      my $child = shift;
4270      if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
          table => 1, tbody => 1, tfoot => 1, thead => 1, tr => 1,  
         }->{$self->{open_elements}->[-1]->[1]}) {  
4271        # MUST        # MUST
4272        my $foster_parent_element;        my $foster_parent_element;
4273        my $next_sibling;        my $next_sibling;
4274                           OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4275                             if ($self->{open_elements}->[$_]->[1] eq 'table') {          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4276                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4277                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4278                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 3254  sub _tree_construction_main ($) { Line 4297  sub _tree_construction_main ($) {
4297      }      }
4298    }; # $insert_to_foster    }; # $insert_to_foster
4299    
4300    B: {    ## NOTE: Insert a character (MUST): When a character is inserted, if
4301      ## the last node that was inserted by the parser is a Text node and
4302      ## the character has to be inserted after that node, then the
4303      ## character is appended to the Text node.  However, if any other
4304      ## node is inserted by the parser, then a new Text node is created
4305      ## and the character is appended as that Text node.  If I'm not
4306      ## wrong, for a parser with scripting disabled, there are only two
4307      ## cases where this occurs.  One is the case where an element node
4308      ## is inserted to the |head| element.  This is covered by using the
4309      ## |$self->{head_element_inserted}| flag.  Another is the case where
4310      ## an element or comment is inserted into the |table| subtree while
4311      ## foster parenting happens.  This is covered by using the [2] flag
4312      ## of the |$open_tables| structure.  All other cases are handled
4313      ## simply by calling |manakai_append_text| method.
4314    
4315      ## TODO: |<body><script>document.write("a<br>");
4316      ## document.body.removeChild (document.body.lastChild);
4317      ## document.write ("b")</script>|
4318    
4319      B: while (1) {
4320      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4321        !!!cp ('t73');        !!!cp ('t73');
4322        !!!parse-error (type => 'DOCTYPE in the middle');        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4323        ## Ignore the token        ## Ignore the token
4324        ## Stay in the phase        ## Stay in the phase
4325        !!!next-token;        !!!next-token;
4326        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;  
4327      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4328               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4329        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4330          !!!cp ('t79');          !!!cp ('t79');
4331          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4332          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4333        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4334          !!!cp ('t80');          !!!cp ('t80');
4335          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4336          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4337        } else {        } else {
4338          !!!cp ('t81');          !!!cp ('t81');
4339        }        }
4340    
4341        !!!cp ('t82');        !!!cp ('t82');
4342        !!!parse-error (type => 'not first start tag');        !!!parse-error (type => 'not first start tag', token => $token);
4343        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4344        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4345          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
# Line 3319  sub _tree_construction_main ($) { Line 4349  sub _tree_construction_main ($) {
4349               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4350          }          }
4351        }        }
4352          !!!nack ('t84.1');
4353        !!!next-token;        !!!next-token;
4354        redo B;        next B;
4355      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4356        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4357        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
# Line 3332  sub _tree_construction_main ($) { Line 4363  sub _tree_construction_main ($) {
4363        } else {        } else {
4364          !!!cp ('t87');          !!!cp ('t87');
4365          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4366            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4367        }        }
4368        !!!next-token;        !!!next-token;
4369        redo B;        next B;
4370      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4371          if ($token->{type} == CHARACTER_TOKEN) {
4372            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4373            delete $self->{ignore_newline};
4374    
4375            if (length $token->{data}) {
4376              !!!cp ('t43');
4377              $self->{open_elements}->[-1]->[0]->manakai_append_text
4378                  ($token->{data});
4379            } else {
4380              !!!cp ('t43.1');
4381            }
4382            !!!next-token;
4383            next B;
4384          } elsif ($token->{type} == END_TAG_TOKEN) {
4385            delete $self->{ignore_newline};
4386    
4387            if ($token->{tag_name} eq 'script') {
4388              !!!cp ('t50');
4389              
4390              ## Para 1-2
4391              my $script = pop @{$self->{open_elements}};
4392              
4393              ## Para 3
4394              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4395    
4396              ## Para 4
4397              ## TODO: $old_insertion_point = $current_insertion_point;
4398              ## TODO: $current_insertion_point = just before $self->{nc};
4399    
4400              ## Para 5
4401              ## TODO: Run the $script->[0].
4402    
4403              ## Para 6
4404              ## TODO: $current_insertion_point = $old_insertion_point;
4405    
4406              ## Para 7
4407              ## TODO: if ($pending_external_script) {
4408                ## TODO: ...
4409              ## TODO: }
4410    
4411              !!!next-token;
4412              next B;
4413            } else {
4414              !!!cp ('t42');
4415    
4416              pop @{$self->{open_elements}};
4417    
4418              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4419              !!!next-token;
4420              next B;
4421            }
4422          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4423            delete $self->{ignore_newline};
4424    
4425            !!!cp ('t44');
4426            !!!parse-error (type => 'not closed',
4427                            text => $self->{open_elements}->[-1]->[0]
4428                                ->manakai_local_name,
4429                            token => $token);
4430    
4431            #if ($self->{open_elements}->[-1]->[1] & SCRIPT_EL) {
4432            #  ## TODO: Mark as "already executed"
4433            #}
4434    
4435            pop @{$self->{open_elements}};
4436    
4437            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4438            ## Reprocess.
4439            next B;
4440          } else {
4441            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4442          }
4443        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4444          if ($token->{type} == CHARACTER_TOKEN) {
4445            !!!cp ('t87.1');
4446            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4447            !!!next-token;
4448            next B;
4449          } elsif ($token->{type} == START_TAG_TOKEN) {
4450            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4451                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4452                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4453                ($token->{tag_name} eq 'svg' and
4454                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4455              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4456              !!!cp ('t87.2');
4457              #
4458            } elsif ({
4459                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4460                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4461                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4462                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4463                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4464                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4465                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4466                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4467                     }->{$token->{tag_name}}) {
4468              !!!cp ('t87.2');
4469              !!!parse-error (type => 'not closed',
4470                              text => $self->{open_elements}->[-1]->[0]
4471                                  ->manakai_local_name,
4472                              token => $token);
4473    
4474              pop @{$self->{open_elements}}
4475                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4476    
4477              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4478              ## Reprocess.
4479              next B;
4480            } else {
4481              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4482              my $tag_name = $token->{tag_name};
4483              if ($nsuri eq $SVG_NS) {
4484                $tag_name = {
4485                   altglyph => 'altGlyph',
4486                   altglyphdef => 'altGlyphDef',
4487                   altglyphitem => 'altGlyphItem',
4488                   animatecolor => 'animateColor',
4489                   animatemotion => 'animateMotion',
4490                   animatetransform => 'animateTransform',
4491                   clippath => 'clipPath',
4492                   feblend => 'feBlend',
4493                   fecolormatrix => 'feColorMatrix',
4494                   fecomponenttransfer => 'feComponentTransfer',
4495                   fecomposite => 'feComposite',
4496                   feconvolvematrix => 'feConvolveMatrix',
4497                   fediffuselighting => 'feDiffuseLighting',
4498                   fedisplacementmap => 'feDisplacementMap',
4499                   fedistantlight => 'feDistantLight',
4500                   feflood => 'feFlood',
4501                   fefunca => 'feFuncA',
4502                   fefuncb => 'feFuncB',
4503                   fefuncg => 'feFuncG',
4504                   fefuncr => 'feFuncR',
4505                   fegaussianblur => 'feGaussianBlur',
4506                   feimage => 'feImage',
4507                   femerge => 'feMerge',
4508                   femergenode => 'feMergeNode',
4509                   femorphology => 'feMorphology',
4510                   feoffset => 'feOffset',
4511                   fepointlight => 'fePointLight',
4512                   fespecularlighting => 'feSpecularLighting',
4513                   fespotlight => 'feSpotLight',
4514                   fetile => 'feTile',
4515                   feturbulence => 'feTurbulence',
4516                   foreignobject => 'foreignObject',
4517                   glyphref => 'glyphRef',
4518                   lineargradient => 'linearGradient',
4519                   radialgradient => 'radialGradient',
4520                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4521                   textpath => 'textPath',  
4522                }->{$tag_name} || $tag_name;
4523              }
4524    
4525              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4526    
4527              ## "adjust foreign attributes" - done in insert-element-f
4528    
4529              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4530    
4531              if ($self->{self_closing}) {
4532                pop @{$self->{open_elements}};
4533                !!!ack ('t87.3');
4534              } else {
4535                !!!cp ('t87.4');
4536              }
4537    
4538              !!!next-token;
4539              next B;
4540            }
4541          } elsif ($token->{type} == END_TAG_TOKEN) {
4542            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4543            !!!cp ('t87.5');
4544            #
4545          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4546            !!!cp ('t87.6');
4547            !!!parse-error (type => 'not closed',
4548                            text => $self->{open_elements}->[-1]->[0]
4549                                ->manakai_local_name,
4550                            token => $token);
4551    
4552            pop @{$self->{open_elements}}
4553                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4554    
4555            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4556    
4557            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4558            ## Reprocess.
4559            next B;
4560          } else {
4561            die "$0: $token->{type}: Unknown token type";        
4562          }
4563        }
4564    
4565        if ($self->{insertion_mode} & HEAD_IMS) {
4566        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4567          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4568            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4569              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
4570              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
4571                  $self->{open_elements}->[-1]->[0]->append_child
4572                    ($self->{document}->create_text_node ($1));
4573                  delete $self->{head_element_inserted};
4574                  ## NOTE: |</head> <link> |
4575                  #
4576                } else {
4577                  !!!cp ('t88.2');
4578                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4579                  ## NOTE: |</head> &#x20;|
4580                  #
4581                }
4582            } else {            } else {
4583              !!!cp ('t88.1');              !!!cp ('t88.1');
4584              ## Ignore the token.              ## Ignore the token.
4585              !!!next-token;              #
             redo B;  
4586            }            }
4587            unless (length $token->{data}) {            unless (length $token->{data}) {
4588              !!!cp ('t88');              !!!cp ('t88');
4589              !!!next-token;              !!!next-token;
4590              redo B;              next B;
4591            }            }
4592    ## TODO: set $token->{column} appropriately
4593          }          }
4594    
4595          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4596            !!!cp ('t89');            !!!cp ('t89');
4597            ## As if <head>            ## As if <head>
4598            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4599            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4600            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4601                  [$self->{head_element}, $el_category->{head}];
4602    
4603            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4604            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 3369  sub _tree_construction_main ($) { Line 4608  sub _tree_construction_main ($) {
4608            !!!cp ('t90');            !!!cp ('t90');
4609            ## As if </noscript>            ## As if </noscript>
4610            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4611            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4612                        
4613            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4614            ## As if </head>            ## As if </head>
# Line 3385  sub _tree_construction_main ($) { Line 4624  sub _tree_construction_main ($) {
4624            !!!cp ('t92');            !!!cp ('t92');
4625          }          }
4626    
4627              ## "after head" insertion mode          ## "after head" insertion mode
4628              ## As if <body>          ## As if <body>
4629              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4630              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4631              ## reprocess          ## reprocess
4632              redo B;          next B;
4633            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4634              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4635                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4636                  !!!cp ('t93');              !!!cp ('t93');
4637                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4638                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child
4639                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];                  ($self->{head_element});
4640                  $self->{insertion_mode} = IN_HEAD_IM;              push @{$self->{open_elements}},
4641                  !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4642                  redo B;              $self->{insertion_mode} = IN_HEAD_IM;
4643                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!nack ('t93.1');
4644                  !!!cp ('t94');              !!!next-token;
4645                  #              next B;
4646                } else {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4647                  !!!cp ('t95');              !!!cp ('t93.2');
4648                  !!!parse-error (type => 'in head:head'); # or in head noscript              !!!parse-error (type => 'after head', text => 'head',
4649                  ## Ignore the token                              token => $token);
4650                  !!!next-token;              ## Ignore the token
4651                  redo B;              !!!nack ('t93.3');
4652                }              !!!next-token;
4653              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              next B;
4654                !!!cp ('t96');            } else {
4655                ## As if <head>              !!!cp ('t95');
4656                !!!create-element ($self->{head_element}, 'head');              !!!parse-error (type => 'in head:head',
4657                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token); # or in head noscript
4658                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4659                !!!nack ('t95.1');
4660                !!!next-token;
4661                next B;
4662              }
4663            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4664              !!!cp ('t96');
4665              ## As if <head>
4666              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4667              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4668              push @{$self->{open_elements}},
4669                  [$self->{head_element}, $el_category->{head}];
4670    
4671                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4672                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4673              } else {          } else {
4674                !!!cp ('t97');            !!!cp ('t97');
4675              }          }
4676    
4677              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4678                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4679                  !!!cp ('t98');              !!!cp ('t98');
4680                  ## As if </noscript>              ## As if </noscript>
4681                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4682                  !!!parse-error (type => 'in noscript:base');              !!!parse-error (type => 'in noscript', text => 'base',
4683                                              token => $token);
4684                  $self->{insertion_mode} = IN_HEAD_IM;            
4685                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4686                } else {              ## Reprocess in the "in head" insertion mode...
4687                  !!!cp ('t99');            } else {
4688                }              !!!cp ('t99');
4689              }
4690    
4691                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4692                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4693                  !!!cp ('t100');              !!!cp ('t100');
4694                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4695                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4696                } else {              push @{$self->{open_elements}},
4697                  !!!cp ('t101');                  [$self->{head_element}, $el_category->{head}];
4698                }              $self->{head_element_inserted} = 1;
4699                !!!insert-element ($token->{tag_name}, $token->{attributes});            } else {
4700                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t101');
4701                pop @{$self->{open_elements}} # <head>            }
4702                    if $self->{insertion_mode} == AFTER_HEAD_IM;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4703                !!!next-token;            pop @{$self->{open_elements}};
4704                redo B;            pop @{$self->{open_elements}} # <head>
4705              } elsif ($token->{tag_name} eq 'link') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4706                ## NOTE: There is a "as if in head" code clone.            !!!nack ('t101.1');
4707                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            !!!next-token;
4708                  !!!cp ('t102');            next B;
4709                  !!!parse-error (type => 'after head:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'link') {
4710                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4711                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4712                  !!!cp ('t103');              !!!cp ('t102');
4713                }              !!!parse-error (type => 'after head',
4714                !!!insert-element ($token->{tag_name}, $token->{attributes});                              text => $token->{tag_name}, token => $token);
4715                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              push @{$self->{open_elements}},
4716                pop @{$self->{open_elements}} # <head>                  [$self->{head_element}, $el_category->{head}];
4717                    if $self->{insertion_mode} == AFTER_HEAD_IM;              $self->{head_element_inserted} = 1;
4718                !!!next-token;            } else {
4719                redo B;              !!!cp ('t103');
4720              } elsif ($token->{tag_name} eq 'meta') {            }
4721                ## NOTE: There is a "as if in head" code clone.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4722                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}};
4723                  !!!cp ('t104');            pop @{$self->{open_elements}} # <head>
4724                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4725                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!ack ('t103.1');
4726                } else {            !!!next-token;
4727                  !!!cp ('t105');            next B;
4728                }          } elsif ($token->{tag_name} eq 'command' or
4729                !!!insert-element ($token->{tag_name}, $token->{attributes});                   $token->{tag_name} eq 'eventsource') {
4730                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4731                ## NOTE: If the insertion mode at the time of the emission
4732                ## of the token was "before head", $self->{insertion_mode}
4733                ## is already changed to |IN_HEAD_IM|.
4734    
4735                ## NOTE: There is a "as if in head" code clone.
4736                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4737                pop @{$self->{open_elements}};
4738                pop @{$self->{open_elements}} # <head>
4739                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4740                !!!ack ('t103.2');
4741                !!!next-token;
4742                next B;
4743              } else {
4744                ## NOTE: "in head noscript" or "after head" insertion mode
4745                ## - in these cases, these tags are treated as same as
4746                ## normal in-body tags.
4747                !!!cp ('t103.3');
4748                #
4749              }
4750            } elsif ($token->{tag_name} eq 'meta') {
4751              ## NOTE: There is a "as if in head" code clone.
4752              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4753                !!!cp ('t104');
4754                !!!parse-error (type => 'after head',
4755                                text => $token->{tag_name}, token => $token);
4756                push @{$self->{open_elements}},
4757                    [$self->{head_element}, $el_category->{head}];
4758                $self->{head_element_inserted} = 1;
4759              } else {
4760                !!!cp ('t105');
4761              }
4762              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4763              my $meta_el = pop @{$self->{open_elements}};
4764    
4765                unless ($self->{confident}) {                unless ($self->{confident}) {
4766                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4767                    !!!cp ('t106');                    !!!cp ('t106');
4768                      ## NOTE: Whether the encoding is supported or not is handled
4769                      ## in the {change_encoding} callback.
4770                    $self->{change_encoding}                    $self->{change_encoding}
4771                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4772                             $token);
4773                                        
4774                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4775                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4776                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4777                                                 ->{has_reference});                                                 ->{has_reference});
4778                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4779                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4780                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4781                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4782                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4783                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4784                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4785                      !!!cp ('t107');                      !!!cp ('t107');
4786                        ## NOTE: Whether the encoding is supported or not is handled
4787                        ## in the {change_encoding} callback.
4788                      $self->{change_encoding}                      $self->{change_encoding}
4789                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4790                               $token);
4791                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4792                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4793                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
# Line 3525  sub _tree_construction_main ($) { Line 4815  sub _tree_construction_main ($) {
4815    
4816                pop @{$self->{open_elements}} # <head>                pop @{$self->{open_elements}} # <head>
4817                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4818                  !!!ack ('t110.1');
4819                !!!next-token;                !!!next-token;
4820                redo B;                next B;
4821              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4822                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4823                  !!!cp ('t111');              !!!cp ('t111');
4824                  ## As if </noscript>              ## As if </noscript>
4825                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4826                  !!!parse-error (type => 'in noscript:title');              !!!parse-error (type => 'in noscript', text => 'title',
4827                                              token => $token);
4828                  $self->{insertion_mode} = IN_HEAD_IM;            
4829                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4830                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4831                  !!!cp ('t112');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4832                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t112');
4833                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4834                } else {                              text => $token->{tag_name}, token => $token);
4835                  !!!cp ('t113');              push @{$self->{open_elements}},
4836                }                  [$self->{head_element}, $el_category->{head}];
4837                $self->{head_element_inserted} = 1;
4838                ## NOTE: There is a "as if in head" code clone.            } else {
4839                my $parent = defined $self->{head_element} ? $self->{head_element}              !!!cp ('t113');
4840                    : $self->{open_elements}->[-1]->[0];            }
4841                $parse_rcdata->(RCDATA_CONTENT_MODEL);  
4842                pop @{$self->{open_elements}} # <head>            ## NOTE: There is a "as if in head" code clone.
4843                    if $self->{insertion_mode} == AFTER_HEAD_IM;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4844                redo B;            ## ISSUE: A spec bug [Bug 6038]
4845              } elsif ($token->{tag_name} eq 'style') {            splice @{$self->{open_elements}}, -2, 1, () # <head>
4846                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4847                ## insertion mode IN_HEAD_IM)            next B;
4848                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'style' or
4849                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                   $token->{tag_name} eq 'noframes') {
4850                  !!!cp ('t114');            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4851                  !!!parse-error (type => 'after head:'.$token->{tag_name});            ## insertion mode IN_HEAD_IM)
4852                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            ## NOTE: There is a "as if in head" code clone.
4853                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4854                  !!!cp ('t115');              !!!cp ('t114');
4855                }              !!!parse-error (type => 'after head',
4856                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
4857                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
4858                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
4859                redo B;              $self->{head_element_inserted} = 1;
4860              } elsif ($token->{tag_name} eq 'noscript') {            } else {
4861                !!!cp ('t115');
4862              }
4863              $parse_rcdata->(CDATA_CONTENT_MODEL);
4864              ## ISSUE: A spec bug [Bug 6038]
4865              splice @{$self->{open_elements}}, -2, 1, () # <head>
4866                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4867              next B;
4868            } elsif ($token->{tag_name} eq 'noscript') {
4869                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4870                  !!!cp ('t116');                  !!!cp ('t116');
4871                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4872                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4873                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4874                    !!!nack ('t116.1');
4875                  !!!next-token;                  !!!next-token;
4876                  redo B;                  next B;
4877                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4878                  !!!cp ('t117');                  !!!cp ('t117');
4879                  !!!parse-error (type => 'in noscript:noscript');                  !!!parse-error (type => 'in noscript', text => 'noscript',
4880                                    token => $token);
4881                  ## Ignore the token                  ## Ignore the token
4882                    !!!nack ('t117.1');
4883                  !!!next-token;                  !!!next-token;
4884                  redo B;                  next B;
4885                } else {                } else {
4886                  !!!cp ('t118');                  !!!cp ('t118');
4887                  #                  #
4888                }                }
4889              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4890                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4891                  !!!cp ('t119');              !!!cp ('t119');
4892                  ## As if </noscript>              ## As if </noscript>
4893                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4894                  !!!parse-error (type => 'in noscript:script');              !!!parse-error (type => 'in noscript', text => 'script',
4895                                              token => $token);
4896                  $self->{insertion_mode} = IN_HEAD_IM;            
4897                  ## Reprocess in the "in head" insertion mode...              $self->{insertion_mode} = IN_HEAD_IM;
4898                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              ## Reprocess in the "in head" insertion mode...
4899                  !!!cp ('t120');            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4900                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t120');
4901                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4902                } else {                              text => $token->{tag_name}, token => $token);
4903                  !!!cp ('t121');              push @{$self->{open_elements}},
4904                }                  [$self->{head_element}, $el_category->{head}];
4905                $self->{head_element_inserted} = 1;
4906                ## NOTE: There is a "as if in head" code clone.            } else {
4907                $script_start_tag->();              !!!cp ('t121');
4908                pop @{$self->{open_elements}} # <head>            }
4909                    if $self->{insertion_mode} == AFTER_HEAD_IM;  
4910                redo B;            ## NOTE: There is a "as if in head" code clone.
4911              } elsif ($token->{tag_name} eq 'body' or            $script_start_tag->();
4912                       $token->{tag_name} eq 'frameset') {            ## ISSUE: A spec bug  [Bug 6038]
4913              splice @{$self->{open_elements}}, -2, 1 # <head>
4914                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4915              next B;
4916            } elsif ($token->{tag_name} eq 'body' or
4917                     $token->{tag_name} eq 'frameset') {
4918                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4919                  !!!cp ('t122');                  !!!cp ('t122');
4920                  ## As if </noscript>                  ## As if </noscript>
4921                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4922                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4923                                    text => $token->{tag_name}, token => $token);
4924                                    
4925                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4926                  ## As if </head>                  ## As if </head>
# Line 3629  sub _tree_construction_main ($) { Line 4937  sub _tree_construction_main ($) {
4937                }                }
4938    
4939                ## "after head" insertion mode                ## "after head" insertion mode
4940                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4941                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4942                  !!!cp ('t126');                  !!!cp ('t126');
4943                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
# Line 3639  sub _tree_construction_main ($) { Line 4947  sub _tree_construction_main ($) {
4947                } else {                } else {
4948                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4949                }                }
4950                  !!!nack ('t127.1');
4951                !!!next-token;                !!!next-token;
4952                redo B;                next B;
4953              } else {              } else {
4954                !!!cp ('t128');                !!!cp ('t128');
4955                #                #
# Line 3650  sub _tree_construction_main ($) { Line 4959  sub _tree_construction_main ($) {
4959                !!!cp ('t129');                !!!cp ('t129');
4960                ## As if </noscript>                ## As if </noscript>
4961                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4962                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4963                                  text => $token->{tag_name}, token => $token);
4964                                
4965                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4966                ## As if </head>                ## As if </head>
# Line 3669  sub _tree_construction_main ($) { Line 4979  sub _tree_construction_main ($) {
4979    
4980              ## "after head" insertion mode              ## "after head" insertion mode
4981              ## As if <body>              ## As if <body>
4982              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4983              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4984              ## reprocess              ## reprocess
4985              redo B;              !!!ack-later;
4986                next B;
4987            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4988              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4989                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4990                  !!!cp ('t132');                  !!!cp ('t132');
4991                  ## As if <head>                  ## As if <head>
4992                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4993                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4994                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4995                        [$self->{head_element}, $el_category->{head}];
4996    
4997                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4998                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4999                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5000                  !!!next-token;                  !!!next-token;
5001                  redo B;                  next B;
5002                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5003                  !!!cp ('t133');                  !!!cp ('t133');
5004                  ## As if </noscript>                  ## As if </noscript>
5005                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5006                  !!!parse-error (type => 'in noscript:/head');                  !!!parse-error (type => 'in noscript:/',
5007                                    text => 'head', token => $token);
5008                                    
5009                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5011                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5012                  !!!next-token;                  !!!next-token;
5013                  redo B;                  next B;
5014                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5015                  !!!cp ('t134');                  !!!cp ('t134');
5016                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5017                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5018                  !!!next-token;                  !!!next-token;
5019                  redo B;                  next B;
5020                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021                    !!!cp ('t134.1');
5022                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5023                                    token => $token);
5024                    ## Ignore the token
5025                    !!!next-token;
5026                    next B;
5027                } else {                } else {
5028                  !!!cp ('t135');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 #  
5029                }                }
5030              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5031                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
# Line 3714  sub _tree_construction_main ($) { Line 5033  sub _tree_construction_main ($) {
5033                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5034                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5035                  !!!next-token;                  !!!next-token;
5036                  redo B;                  next B;
5037                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5038                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5039                  !!!cp ('t137');                  !!!cp ('t137');
5040                  !!!parse-error (type => 'unmatched end tag:noscript');                  !!!parse-error (type => 'unmatched end tag',
5041                                    text => 'noscript', token => $token);
5042                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5043                  !!!next-token;                  !!!next-token;
5044                  redo B;                  next B;
5045                } else {                } else {
5046                  !!!cp ('t138');                  !!!cp ('t138');
5047                  #                  #
# Line 3728  sub _tree_construction_main ($) { Line 5049  sub _tree_construction_main ($) {
5049              } elsif ({              } elsif ({
5050                        body => 1, html => 1,                        body => 1, html => 1,
5051                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5052                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5053                  !!!cp ('t139');                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5054                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5055                  !!!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) {  
5056                  !!!cp ('t140');                  !!!cp ('t140');
5057                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5058                                    text => $token->{tag_name}, token => $token);
5059                  ## Ignore the token                  ## Ignore the token
5060                  !!!next-token;                  !!!next-token;
5061                  redo B;                  next B;
5062                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5063                    !!!cp ('t140.1');
5064                    !!!parse-error (type => 'unmatched end tag',
5065                                    text => $token->{tag_name}, token => $token);
5066                    ## Ignore the token
5067                    !!!next-token;
5068                    next B;
5069                } else {                } else {
5070                  !!!cp ('t141');                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5071                }                }
5072                              } elsif ($token->{tag_name} eq 'p') {
5073                #                !!!cp ('t142');
5074              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5075                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5076                       }->{$token->{tag_name}}) {                ## Ignore the token
5077                  !!!next-token;
5078                  next B;
5079                } elsif ($token->{tag_name} eq 'br') {
5080                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5081                  !!!cp ('t142');                  !!!cp ('t142.2');
5082                  ## As if <head>                  ## (before head) as if <head>, (in head) as if </head>
5083                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5084                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5085                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5086      
5087                    ## Reprocess in the "after head" insertion mode...
5088                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5089                    !!!cp ('t143.2');
5090                    ## As if </head>
5091                    pop @{$self->{open_elements}};
5092                    $self->{insertion_mode} = AFTER_HEAD_IM;
5093      
5094                    ## Reprocess in the "after head" insertion mode...
5095                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5096                    !!!cp ('t143.3');
5097                    ## ISSUE: Two parse errors for <head><noscript></br>
5098                    !!!parse-error (type => 'unmatched end tag',
5099                                    text => 'br', token => $token);
5100                    ## As if </noscript>
5101                    pop @{$self->{open_elements}};
5102                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5103    
5104                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5105                } else {                  ## As if </head>
5106                  !!!cp ('t143');                  pop @{$self->{open_elements}};
5107                }                  $self->{insertion_mode} = AFTER_HEAD_IM;
5108    
5109                #                  ## Reprocess in the "after head" insertion mode...
5110              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5111                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
                 !!!cp ('t144');  
5112                  #                  #
5113                } else {                } else {
5114                  !!!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;  
5115                }                }
5116    
5117                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5118                  !!!parse-error (type => 'unmatched end tag',
5119                                  text => 'br', token => $token);
5120                  ## Ignore the token
5121                  !!!next-token;
5122                  next B;
5123                } else {
5124                  !!!cp ('t145');
5125                  !!!parse-error (type => 'unmatched end tag',
5126                                  text => $token->{tag_name}, token => $token);
5127                  ## Ignore the token
5128                  !!!next-token;
5129                  next B;
5130              }              }
5131    
5132              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5133                !!!cp ('t146');                !!!cp ('t146');
5134                ## As if </noscript>                ## As if </noscript>
5135                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5136                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5137                                  text => $token->{tag_name}, token => $token);
5138                                
5139                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5140                ## As if </head>                ## As if </head>
# Line 3798  sub _tree_construction_main ($) { Line 5150  sub _tree_construction_main ($) {
5150              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5151  ## ISSUE: This case cannot be reached?  ## ISSUE: This case cannot be reached?
5152                !!!cp ('t148');                !!!cp ('t148');
5153                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5154                                  text => $token->{tag_name}, token => $token);
5155                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5156                !!!next-token;                !!!next-token;
5157                redo B;                next B;
5158              } else {              } else {
5159                !!!cp ('t149');                !!!cp ('t149');
5160              }              }
5161    
5162              ## "after head" insertion mode              ## "after head" insertion mode
5163              ## As if <body>              ## As if <body>
5164              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5165              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5166              ## reprocess              ## reprocess
5167              redo B;              next B;
5168            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5169              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5170            }            !!!cp ('t149.1');
5171    
5172              ## NOTE: As if <head>
5173              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5174              $self->{open_elements}->[-1]->[0]->append_child
5175                  ($self->{head_element});
5176              #push @{$self->{open_elements}},
5177              #    [$self->{head_element}, $el_category->{head}];
5178              #$self->{insertion_mode} = IN_HEAD_IM;
5179              ## NOTE: Reprocess.
5180    
5181              ## NOTE: As if </head>
5182              #pop @{$self->{open_elements}};
5183              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5184              ## NOTE: Reprocess.
5185              
5186              #
5187            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5188              !!!cp ('t149.2');
5189    
5190              ## NOTE: As if </head>
5191              pop @{$self->{open_elements}};
5192              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5193              ## NOTE: Reprocess.
5194    
5195              #
5196            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5197              !!!cp ('t149.3');
5198    
5199              !!!parse-error (type => 'in noscript:#eof', token => $token);
5200    
5201              ## As if </noscript>
5202              pop @{$self->{open_elements}};
5203              #$self->{insertion_mode} = IN_HEAD_IM;
5204              ## NOTE: Reprocess.
5205    
5206              ## NOTE: As if </head>
5207              pop @{$self->{open_elements}};
5208              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5209              ## NOTE: Reprocess.
5210    
5211              #
5212            } else {
5213              !!!cp ('t149.4');
5214              #
5215            }
5216    
5217            ## ISSUE: An issue in the spec.          ## NOTE: As if <body>
5218            !!!insert-element ('body',, $token);
5219            $self->{insertion_mode} = IN_BODY_IM;
5220            ## NOTE: Reprocess.
5221            next B;
5222          } else {
5223            die "$0: $token->{type}: Unknown token type";
5224          }
5225      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5226            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5227              !!!cp ('t150');              !!!cp ('t150');
# Line 3826  sub _tree_construction_main ($) { Line 5231  sub _tree_construction_main ($) {
5231              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5232    
5233              !!!next-token;              !!!next-token;
5234              redo B;              next B;
5235            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5236              if ({              if ({
5237                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3834  sub _tree_construction_main ($) { Line 5239  sub _tree_construction_main ($) {
5239                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5240                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5241                  ## have an element in table scope                  ## have an element in table scope
5242                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5243                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5244                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5245                      !!!cp ('t151');                      !!!cp ('t151');
5246                      $tn = $node->[1];  
5247                      last INSCOPE;                      ## Close the cell
5248                    } elsif ({                      !!!back-token; # <x>
5249                              table => 1, html => 1,                      $token = {type => END_TAG_TOKEN,
5250                             }->{$node->[1]}) {                                tag_name => $node->[0]->manakai_local_name,
5251                                  line => $token->{line},
5252                                  column => $token->{column}};
5253                        next B;
5254                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5255                      !!!cp ('t152');                      !!!cp ('t152');
5256                      last INSCOPE;                      ## ISSUE: This case can never be reached, maybe.
5257                        last;
5258                    }                    }
5259                  } # INSCOPE                  }
5260                    unless (defined $tn) {  
5261                      !!!cp ('t153');                  !!!cp ('t153');
5262  ## TODO: This error type is wrong.                  !!!parse-error (type => 'start tag not allowed',
5263                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      text => $token->{tag_name}, token => $token);
5264                      ## Ignore the token                  ## Ignore the token
5265                      !!!next-token;                  !!!nack ('t153.1');
5266                      redo B;                  !!!next-token;
5267                    }                  next B;
                   
                 !!!cp ('t154');  
                 ## Close the cell  
                 !!!back-token; # <?>  
                 $token = {type => END_TAG_TOKEN, tag_name => $tn};  
                 redo B;  
5268                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5269                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5270                                    token => $token);
5271                                    
5272                  ## As if </caption>                  ## NOTE: As if </caption>.
5273                  ## have a table element in table scope                  ## have a table element in table scope
5274                  my $i;                  my $i;
5275                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5276                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5277                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5278                      !!!cp ('t155');                      if ($node->[1] & CAPTION_EL) {
5279                      $i = $_;                        !!!cp ('t155');
5280                      last INSCOPE;                        $i = $_;
5281                    } elsif ({                        last INSCOPE;
5282                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5283                             }->{$node->[1]}) {                        !!!cp ('t156');
5284                      !!!cp ('t156');                        last;
5285                      last INSCOPE;                      }
5286                    }                    }
5287    
5288                      !!!cp ('t157');
5289                      !!!parse-error (type => 'start tag not allowed',
5290                                      text => $token->{tag_name}, token => $token);
5291                      ## Ignore the token
5292                      !!!nack ('t157.1');
5293                      !!!next-token;
5294                      next B;
5295                  } # 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;  
                   }  
5296                                    
5297                  ## generate implied end tags                  ## generate implied end tags
5298                  while ({                  while ($self->{open_elements}->[-1]->[1]
5299                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5300                    !!!cp ('t158');                    !!!cp ('t158');
5301                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5302                  }                  }
5303    
5304                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5305                    !!!cp ('t159');                    !!!cp ('t159');
5306                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5307                                      text => $self->{open_elements}->[-1]->[0]
5308                                          ->manakai_local_name,
5309                                      token => $token);
5310                  } else {                  } else {
5311                    !!!cp ('t160');                    !!!cp ('t160');
5312                  }                  }
# Line 3912  sub _tree_construction_main ($) { Line 5318  sub _tree_construction_main ($) {
5318                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5319                                    
5320                  ## reprocess                  ## reprocess
5321                  redo B;                  !!!ack-later;
5322                    next B;
5323                } else {                } else {
5324                  !!!cp ('t161');                  !!!cp ('t161');
5325                  #                  #
# Line 3928  sub _tree_construction_main ($) { Line 5335  sub _tree_construction_main ($) {
5335                  my $i;                  my $i;
5336                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5337                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5338                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5339                      !!!cp ('t163');                      !!!cp ('t163');
5340                      $i = $_;                      $i = $_;
5341                      last INSCOPE;                      last INSCOPE;
5342                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5343                      !!!cp ('t164');                      !!!cp ('t164');
5344                      last INSCOPE;                      last INSCOPE;
5345                    }                    }
5346                  } # INSCOPE                  } # INSCOPE
5347                    unless (defined $i) {                    unless (defined $i) {
5348                      !!!cp ('t165');                      !!!cp ('t165');
5349                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
5350                                        text => $token->{tag_name},
5351                                        token => $token);
5352                      ## Ignore the token                      ## Ignore the token
5353                      !!!next-token;                      !!!next-token;
5354                      redo B;                      next B;
5355                    }                    }
5356                                    
5357                  ## generate implied end tags                  ## generate implied end tags
5358                  while ({                  while ($self->{open_elements}->[-1]->[1]
5359                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5360                    !!!cp ('t166');                    !!!cp ('t166');
5361                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5362                  }                  }
5363    
5364                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5365                            ne $token->{tag_name}) {
5366                    !!!cp ('t167');                    !!!cp ('t167');
5367                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5368                                      text => $self->{open_elements}->[-1]->[0]
5369                                          ->manakai_local_name,
5370                                      token => $token);
5371                  } else {                  } else {
5372                    !!!cp ('t168');                    !!!cp ('t168');
5373                  }                  }
# Line 3969  sub _tree_construction_main ($) { Line 5379  sub _tree_construction_main ($) {
5379                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5380                                    
5381                  !!!next-token;                  !!!next-token;
5382                  redo B;                  next B;
5383                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5384                  !!!cp ('t169');                  !!!cp ('t169');
5385                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5386                                    text => $token->{tag_name}, token => $token);
5387                  ## Ignore the token                  ## Ignore the token
5388                  !!!next-token;                  !!!next-token;
5389                  redo B;                  next B;
5390                } else {                } else {
5391                  !!!cp ('t170');                  !!!cp ('t170');
5392                  #                  #
# Line 3984  sub _tree_construction_main ($) { Line 5395  sub _tree_construction_main ($) {
5395                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5396                  ## have a table element in table scope                  ## have a table element in table scope
5397                  my $i;                  my $i;
5398                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5399                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5400                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5401                      !!!cp ('t171');                      if ($node->[1] & CAPTION_EL) {
5402                      $i = $_;                        !!!cp ('t171');
5403                      last INSCOPE;                        $i = $_;
5404                    } elsif ({                        last INSCOPE;
5405                              table => 1, html => 1,                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5406                             }->{$node->[1]}) {                        !!!cp ('t172');
5407                      !!!cp ('t172');                        last;
5408                      last INSCOPE;                      }
5409                    }                    }
5410    
5411                      !!!cp ('t173');
5412                      !!!parse-error (type => 'unmatched end tag',
5413                                      text => $token->{tag_name}, token => $token);
5414                      ## Ignore the token
5415                      !!!next-token;
5416                      next B;
5417                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!cp ('t173');  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5418                                    
5419                  ## generate implied end tags                  ## generate implied end tags
5420                  while ({                  while ($self->{open_elements}->[-1]->[1]
5421                          dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
                        }->{$self->{open_elements}->[-1]->[1]}) {  
5422                    !!!cp ('t174');                    !!!cp ('t174');
5423                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5424                  }                  }
5425                                    
5426                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5427                    !!!cp ('t175');                    !!!cp ('t175');
5428                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'not closed',
5429                                      text => $self->{open_elements}->[-1]->[0]
5430                                          ->manakai_local_name,
5431                                      token => $token);
5432                  } else {                  } else {
5433                    !!!cp ('t176');                    !!!cp ('t176');
5434                  }                  }
# Line 4027  sub _tree_construction_main ($) { Line 5440  sub _tree_construction_main ($) {
5440                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5441                                    
5442                  !!!next-token;                  !!!next-token;
5443                  redo B;                  next B;
5444                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5445                  !!!cp ('t177');                  !!!cp ('t177');
5446                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5447                                    text => $token->{tag_name}, token => $token);
5448                  ## Ignore the token                  ## Ignore the token
5449                  !!!next-token;                  !!!next-token;
5450                  redo B;                  next B;
5451                } else {                } else {
5452                  !!!cp ('t178');                  !!!cp ('t178');
5453                  #                  #
# Line 4046  sub _tree_construction_main ($) { Line 5460  sub _tree_construction_main ($) {
5460                ## have an element in table scope                ## have an element in table scope
5461                my $i;                my $i;
5462                my $tn;                my $tn;
5463                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5464                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5465                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5466                    !!!cp ('t179');                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5467                    $i = $_;                      !!!cp ('t179');
5468                    last INSCOPE;                      $i = $_;
5469                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
5470                    !!!cp ('t180');                      ## Close the cell
5471                    $tn = $node->[1];                      !!!back-token; # </x>
5472                    ## NOTE: There is exactly one |td| or |th| element                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5473                    ## in scope in the stack of open elements by definition.                                line => $token->{line},
5474                  } elsif ({                                column => $token->{column}};
5475                            table => 1, html => 1,                      next B;
5476                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_CELL_EL) {
5477                    !!!cp ('t181');                      !!!cp ('t180');
5478                    last INSCOPE;                      $tn = $node->[0]->manakai_local_name;
5479                        ## NOTE: There is exactly one |td| or |th| element
5480                        ## in scope in the stack of open elements by definition.
5481                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5482                        ## ISSUE: Can this be reached?
5483                        !!!cp ('t181');
5484                        last;
5485                      }
5486                  }                  }
5487                } # INSCOPE  
               unless (defined $i) {  
5488                  !!!cp ('t182');                  !!!cp ('t182');
5489                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5490                        text => $token->{tag_name}, token => $token);
5491                  ## Ignore the token                  ## Ignore the token
5492                  !!!next-token;                  !!!next-token;
5493                  redo B;                  next B;
5494                } else {                } # INSCOPE
                 !!!cp ('t183');  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5495              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5496                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5497                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5498                                  token => $token);
5499    
5500                ## As if </caption>                ## As if </caption>
5501                ## have a table element in table scope                ## have a table element in table scope
5502                my $i;                my $i;
5503                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5504                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5505                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5506                    !!!cp ('t184');                    !!!cp ('t184');
5507                    $i = $_;                    $i = $_;
5508                    last INSCOPE;                    last INSCOPE;
5509                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
5510                    !!!cp ('t185');                    !!!cp ('t185');
5511                    last INSCOPE;                    last INSCOPE;
5512                  }                  }
5513                } # INSCOPE                } # INSCOPE
5514                unless (defined $i) {                unless (defined $i) {
5515                  !!!cp ('t186');                  !!!cp ('t186');
5516                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'unmatched end tag',
5517                                    text => 'caption', token => $token);
5518                  ## Ignore the token                  ## Ignore the token
5519                  !!!next-token;                  !!!next-token;
5520                  redo B;                  next B;
5521                }                }
5522                                
5523                ## generate implied end tags                ## generate implied end tags
5524                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5525                  !!!cp ('t187');                  !!!cp ('t187');
5526                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5527                }                }
5528    
5529                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5530                  !!!cp ('t188');                  !!!cp ('t188');
5531                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5532                                    text => $self->{open_elements}->[-1]->[0]
5533                                        ->manakai_local_name,
5534                                    token => $token);
5535                } else {                } else {
5536                  !!!cp ('t189');                  !!!cp ('t189');
5537                }                }
# Line 4128  sub _tree_construction_main ($) { Line 5543  sub _tree_construction_main ($) {
5543                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5544    
5545                ## reprocess                ## reprocess
5546                redo B;                next B;
5547              } elsif ({              } elsif ({
5548                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5549                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5550                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5551                  !!!cp ('t190');                  !!!cp ('t190');
5552                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5553                                    text => $token->{tag_name}, token => $token);
5554                  ## Ignore the token                  ## Ignore the token
5555                  !!!next-token;                  !!!next-token;
5556                  redo B;                  next B;
5557                } else {                } else {
5558                  !!!cp ('t191');                  !!!cp ('t191');
5559                  #                  #
# Line 4148  sub _tree_construction_main ($) { Line 5564  sub _tree_construction_main ($) {
5564                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5565                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5566                !!!cp ('t192');                !!!cp ('t192');
5567                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
5568                                  text => $token->{tag_name}, token => $token);
5569                ## Ignore the token                ## Ignore the token
5570                !!!next-token;                !!!next-token;
5571                redo B;                next B;
5572              } else {              } else {
5573                !!!cp ('t193');                !!!cp ('t193');
5574                #                #
5575              }              }
5576          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5577            for my $entry (@{$self->{open_elements}}) {
5578              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5579                !!!cp ('t75');
5580                !!!parse-error (type => 'in body:#eof', token => $token);
5581                last;
5582              }
5583            }
5584    
5585            ## Stop parsing.
5586            last B;
5587        } else {        } else {
5588          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5589        }        }
# Line 4165  sub _tree_construction_main ($) { Line 5593  sub _tree_construction_main ($) {
5593      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5594        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5595          if (not $open_tables->[-1]->[1] and # tainted          if (not $open_tables->[-1]->[1] and # tainted
5596              $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5597            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5598                                
5599            unless (length $token->{data}) {            unless (length $token->{data}) {
5600              !!!cp ('t194');              !!!cp ('t194');
5601              !!!next-token;              !!!next-token;
5602              redo B;              next B;
5603            } else {            } else {
5604              !!!cp ('t195');              !!!cp ('t195');
5605            }            }
5606          }          }
5607    
5608              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5609    
5610              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5611              ## 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);  
5612                            
5613              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5614                   table => 1, tbody => 1, tfoot => 1,            # MUST
5615                   thead => 1, tr => 1,            my $foster_parent_element;
5616                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5617                # MUST            my $prev_sibling;
5618                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5619                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5620                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5621                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5622                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5623                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5624                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5625                      !!!cp ('t196');                  $prev_sibling = $next_sibling->previous_sibling;
5626                      $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});  
5627                } else {                } else {
5628                  !!!cp ('t199');                  !!!cp ('t197');
5629                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5630                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
5631                     $next_sibling);                  #
5632                }                }
5633                  last OE;
5634                }
5635              } # OE
5636              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5637              $prev_sibling = $foster_parent_element->last_child
5638                  unless defined $foster_parent_element;
5639              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5640              if (defined $prev_sibling and
5641                  $prev_sibling->node_type == 3) {
5642                !!!cp ('t198');
5643                $prev_sibling->manakai_append_text ($token->{data});
5644              } else {
5645                !!!cp ('t199');
5646                $foster_parent_element->insert_before
5647                    ($self->{document}->create_text_node ($token->{data}),
5648                     $next_sibling);
5649              }
5650            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
5651              $open_tables->[-1]->[2] = 1; # ~node inserted
5652          } else {          } else {
5653              ## NOTE: Fragment case or in a foster parent'ed element
5654              ## (e.g. |<table><span>a|).  In fragment case, whether the
5655              ## character is appended to existing node or a new node is
5656              ## created is irrelevant, since the foster parent'ed nodes
5657              ## are discarded and fragment parsing does not invoke any
5658              ## script.
5659            !!!cp ('t200');            !!!cp ('t200');
5660            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
5661                  ($token->{data});
5662          }          }
5663                            
5664          !!!next-token;          !!!next-token;
5665          redo B;          next B;
5666        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5667              if ({          if ({
5668                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5669                   th => 1, td => 1,               th => 1, td => 1,
5670                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5671                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5672                  ## Clear back to table context              ## Clear back to table context
5673                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5674                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5675                    !!!cp ('t201');                !!!cp ('t201');
5676                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5677                  }              }
5678                                
5679                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5680                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5681                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5682                }            }
5683              
5684                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5685                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5686                    !!!cp ('t202');                !!!cp ('t202');
5687                    !!!parse-error (type => 'missing start tag:tr');                !!!parse-error (type => 'missing start tag:tr', token => $token);
5688                  }              }
5689                                    
5690                  ## Clear back to table body context              ## Clear back to table body context
5691                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5692                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5693                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5694                    !!!cp ('t203');                ## ISSUE: Can this case be reached?
5695                    ## ISSUE: Can this case be reached?                pop @{$self->{open_elements}};
5696                    pop @{$self->{open_elements}};              }
                 }  
5697                                    
5698                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5699                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5700                    !!!cp ('t204');                !!!cp ('t204');
5701                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702                    !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703                    redo B;                !!!nack ('t204');
5704                  } else {                !!!next-token;
5705                    !!!cp ('t205');                next B;
5706                    !!!insert-element ('tr');              } else {
5707                    ## reprocess in the "in row" insertion mode                !!!cp ('t205');
5708                  }                !!!insert-element ('tr',, $token);
5709                } else {                ## reprocess in the "in row" insertion mode
5710                  !!!cp ('t206');              }
5711                }            } else {
5712                !!!cp ('t206');
5713              }
5714    
5715                ## Clear back to table row context                ## Clear back to table row context
5716                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5717                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
5718                  !!!cp ('t207');                  !!!cp ('t207');
5719                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5720                }                }
5721                                
5722                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5723                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5724              $self->{insertion_mode} = IN_CELL_IM;
5725    
5726                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5727                                
5728              !!!nack ('t207.1');
5729              !!!next-token;
5730              next B;
5731            } elsif ({
5732                      caption => 1, col => 1, colgroup => 1,
5733                      tbody => 1, tfoot => 1, thead => 1,
5734                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5735                     }->{$token->{tag_name}}) {
5736              if ($self->{insertion_mode} == IN_ROW_IM) {
5737                ## As if </tr>
5738                ## have an element in table scope
5739                my $i;
5740                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5741                  my $node = $self->{open_elements}->[$_];
5742                  if ($node->[1] & TABLE_ROW_EL) {
5743                    !!!cp ('t208');
5744                    $i = $_;
5745                    last INSCOPE;
5746                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5747                    !!!cp ('t209');
5748                    last INSCOPE;
5749                  }
5750                } # INSCOPE
5751                unless (defined $i) {
5752                  !!!cp ('t210');
5753                  ## TODO: This type is wrong.
5754                  !!!parse-error (type => 'unmacthed end tag',
5755                                  text => $token->{tag_name}, token => $token);
5756                  ## Ignore the token
5757                  !!!nack ('t210.1');
5758                !!!next-token;                !!!next-token;
5759                redo B;                next B;
5760              } 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;  
                 }  
5761                                    
5762                  ## Clear back to table row context                  ## Clear back to table row context
5763                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5764                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5765                    !!!cp ('t211');                    !!!cp ('t211');
5766                    ## ISSUE: Can this case be reached?                    ## ISSUE: Can this case be reached?
5767                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4341  sub _tree_construction_main ($) { Line 5772  sub _tree_construction_main ($) {
5772                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5773                    !!!cp ('t212');                    !!!cp ('t212');
5774                    ## reprocess                    ## reprocess
5775                    redo B;                    !!!ack-later;
5776                      next B;
5777                  } else {                  } else {
5778                    !!!cp ('t213');                    !!!cp ('t213');
5779                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
# Line 4353  sub _tree_construction_main ($) { Line 5785  sub _tree_construction_main ($) {
5785                  my $i;                  my $i;
5786                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5787                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5788                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
5789                      !!!cp ('t214');                      !!!cp ('t214');
5790                      $i = $_;                      $i = $_;
5791                      last INSCOPE;                      last INSCOPE;
5792                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
5793                      !!!cp ('t215');                      !!!cp ('t215');
5794                      last INSCOPE;                      last INSCOPE;
5795                    }                    }
5796                  } # INSCOPE                  } # INSCOPE
5797                  unless (defined $i) {                  unless (defined $i) {
5798                    !!!cp ('t216');                    !!!cp ('t216');
5799  ## TODO: This erorr type ios wrong.  ## TODO: This erorr type is wrong.
5800                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
5801                                      text => $token->{tag_name}, token => $token);
5802                    ## Ignore the token                    ## Ignore the token
5803                      !!!nack ('t216.1');
5804                    !!!next-token;                    !!!next-token;
5805                    redo B;                    next B;
5806                  }                  }
5807    
5808                  ## Clear back to table body context                  ## Clear back to table body context
5809                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5810                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5811                    !!!cp ('t217');                    !!!cp ('t217');
5812                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5813                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4398  sub _tree_construction_main ($) { Line 5827  sub _tree_construction_main ($) {
5827                  !!!cp ('t218');                  !!!cp ('t218');
5828                }                }
5829    
5830                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5831                  ## Clear back to table context              ## Clear back to table context
5832                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5833                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5834                    !!!cp ('t219');                !!!cp ('t219');
5835                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
5836                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5837                  }              }
5838                                
5839                  !!!insert-element ('colgroup');              !!!insert-element ('colgroup',, $token);
5840                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5841                  ## reprocess              ## reprocess
5842                  redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5843                } elsif ({              !!!ack-later;
5844                          caption => 1,              next B;
5845                          colgroup => 1,            } elsif ({
5846                          tbody => 1, tfoot => 1, thead => 1,                      caption => 1,
5847                         }->{$token->{tag_name}}) {                      colgroup => 1,
5848                  ## Clear back to table context                      tbody => 1, tfoot => 1, thead => 1,
5849                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                     }->{$token->{tag_name}}) {
5850                         $self->{open_elements}->[-1]->[1] ne 'html') {              ## Clear back to table context
5851                    while (not ($self->{open_elements}->[-1]->[1]
5852                                    & TABLE_SCOPING_EL)) {
5853                    !!!cp ('t220');                    !!!cp ('t220');
5854                    ## ISSUE: Can this state be reached?                    ## ISSUE: Can this state be reached?
5855                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5856                  }                  }
5857                                    
5858                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5859                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5860                                    
5861                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5862                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5863                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5864                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5865                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5866                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5867                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5868                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5869                  !!!next-token;                                        }->{$token->{tag_name}};
5870                  redo B;              !!!next-token;
5871                } else {              !!!nack ('t220.1');
5872                  die "$0: in table: <>: $token->{tag_name}";              next B;
5873                }            } else {
5874                die "$0: in table: <>: $token->{tag_name}";
5875              }
5876              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5877                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5878                                  text => $self->{open_elements}->[-1]->[0]
5879                                      ->manakai_local_name,
5880                                  token => $token);
5881    
5882                ## As if </table>                ## As if </table>
5883                ## have a table element in table scope                ## have a table element in table scope
5884                my $i;                my $i;
5885                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5886                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5887                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5888                    !!!cp ('t221');                    !!!cp ('t221');
5889                    $i = $_;                    $i = $_;
5890                    last INSCOPE;                    last INSCOPE;
5891                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           #table => 1,  
                           html => 1,  
                          }->{$node->[1]}) {  
5892                    !!!cp ('t222');                    !!!cp ('t222');
5893                    last INSCOPE;                    last INSCOPE;
5894                  }                  }
# Line 4463  sub _tree_construction_main ($) { Line 5896  sub _tree_construction_main ($) {
5896                unless (defined $i) {                unless (defined $i) {
5897                  !!!cp ('t223');                  !!!cp ('t223');
5898  ## TODO: The following is wrong, maybe.  ## TODO: The following is wrong, maybe.
5899                  !!!parse-error (type => 'unmatched end tag:table');                  !!!parse-error (type => 'unmatched end tag', text => 'table',
5900                                    token => $token);
5901                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5902                    !!!nack ('t223.1');
5903                  !!!next-token;                  !!!next-token;
5904                  redo B;                  next B;
5905                }                }
5906                                
5907    ## TODO: Followings are removed from the latest spec.
5908                ## generate implied end tags                ## generate implied end tags
5909                while ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                       dd => 1, dt => 1, li => 1, p => 1,  
                      }->{$self->{open_elements}->[-1]->[1]}) {  
5910                  !!!cp ('t224');                  !!!cp ('t224');
5911                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5912                }                }
5913    
5914                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5915                  !!!cp ('t225');                  !!!cp ('t225');
5916  ## ISSUE: Can this case be reached?                  ## NOTE: |<table><tr><table>|
5917                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!parse-error (type => 'not closed',
5918                                    text => $self->{open_elements}->[-1]->[0]
5919                                        ->manakai_local_name,
5920                                    token => $token);
5921                } else {                } else {
5922                  !!!cp ('t226');                  !!!cp ('t226');
5923                }                }
# Line 4490  sub _tree_construction_main ($) { Line 5927  sub _tree_construction_main ($) {
5927    
5928                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5929    
5930                ## reprocess            ## reprocess
5931                redo B;            !!!ack-later;
5932              next B;
5933          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
5934            if (not $open_tables->[-1]->[1]) { # tainted            if (not $open_tables->[-1]->[1]) { # tainted
5935              !!!cp ('t227.8');              !!!cp ('t227.8');
5936              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5937              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
5938              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5939                next B;
5940            } else {            } else {
5941              !!!cp ('t227.7');              !!!cp ('t227.7');
5942              #              #
# Line 4507  sub _tree_construction_main ($) { Line 5946  sub _tree_construction_main ($) {
5946              !!!cp ('t227.6');              !!!cp ('t227.6');
5947              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
5948              $script_start_tag->();              $script_start_tag->();
5949              redo B;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950                next B;
5951            } else {            } else {
5952              !!!cp ('t227.5');              !!!cp ('t227.5');
5953              #              #
# Line 4518  sub _tree_construction_main ($) { Line 5958  sub _tree_construction_main ($) {
5958                my $type = lc $token->{attributes}->{type}->{value};                my $type = lc $token->{attributes}->{type}->{value};
5959                if ($type eq 'hidden') {                if ($type eq 'hidden') {
5960                  !!!cp ('t227.3');                  !!!cp ('t227.3');
5961                  !!!parse-error (type => 'in table:'.$token->{tag_name});                  !!!parse-error (type => 'in table',
5962                                    text => $token->{tag_name}, token => $token);
5963    
5964                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5965                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5966    
5967                  ## TODO: form element pointer                  ## TODO: form element pointer
5968    
5969                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5970    
5971                  !!!next-token;                  !!!next-token;
5972                  redo B;                  !!!ack ('t227.2.1');
5973                    next B;
5974                } else {                } else {
5975                  !!!cp ('t227.2');                  !!!cp ('t227.2');
5976                  #                  #
# Line 4545  sub _tree_construction_main ($) { Line 5988  sub _tree_construction_main ($) {
5988            #            #
5989          }          }
5990    
5991          !!!parse-error (type => 'in table:'.$token->{tag_name});          !!!parse-error (type => 'in table', text => $token->{tag_name},
5992                            token => $token);
5993    
5994          $insert = $insert_to_foster;          $insert = $insert_to_foster;
5995          #          #
# Line 4556  sub _tree_construction_main ($) { Line 6000  sub _tree_construction_main ($) {
6000                my $i;                my $i;
6001                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6002                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6003                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
6004                    !!!cp ('t228');                    !!!cp ('t228');
6005                    $i = $_;                    $i = $_;
6006                    last INSCOPE;                    last INSCOPE;
6007                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6008                    !!!cp ('t229');                    !!!cp ('t229');
6009                    last INSCOPE;                    last INSCOPE;
6010                  }                  }
6011                } # INSCOPE                } # INSCOPE
6012                unless (defined $i) {                unless (defined $i) {
6013                  !!!cp ('t230');                  !!!cp ('t230');
6014                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6015                                    text => $token->{tag_name}, token => $token);
6016                  ## Ignore the token                  ## Ignore the token
6017                    !!!nack ('t230.1');
6018                  !!!next-token;                  !!!next-token;
6019                  redo B;                  next B;
6020                } else {                } else {
6021                  !!!cp ('t232');                  !!!cp ('t232');
6022                }                }
6023    
6024                ## Clear back to table row context                ## Clear back to table row context
6025                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6026                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6027                  !!!cp ('t231');                  !!!cp ('t231');
6028  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6029                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4589  sub _tree_construction_main ($) { Line 6032  sub _tree_construction_main ($) {
6032                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6033                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6034                !!!next-token;                !!!next-token;
6035                redo B;                !!!nack ('t231.1');
6036                  next B;
6037              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6038                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6039                  ## As if </tr>                  ## As if </tr>
# Line 4597  sub _tree_construction_main ($) { Line 6041  sub _tree_construction_main ($) {
6041                  my $i;                  my $i;
6042                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6043                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6044                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6045                      !!!cp ('t233');                      !!!cp ('t233');
6046                      $i = $_;                      $i = $_;
6047                      last INSCOPE;                      last INSCOPE;
6048                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6049                      !!!cp ('t234');                      !!!cp ('t234');
6050                      last INSCOPE;                      last INSCOPE;
6051                    }                    }
# Line 4611  sub _tree_construction_main ($) { Line 6053  sub _tree_construction_main ($) {
6053                  unless (defined $i) {                  unless (defined $i) {
6054                    !!!cp ('t235');                    !!!cp ('t235');
6055  ## TODO: The following is wrong.  ## TODO: The following is wrong.
6056                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!parse-error (type => 'unmatched end tag',
6057                                      text => $token->{type}, token => $token);
6058                    ## Ignore the token                    ## Ignore the token
6059                      !!!nack ('t236.1');
6060                    !!!next-token;                    !!!next-token;
6061                    redo B;                    next B;
6062                  }                  }
6063                                    
6064                  ## Clear back to table row context                  ## Clear back to table row context
6065                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6066                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6067                    !!!cp ('t236');                    !!!cp ('t236');
6068  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6069                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4636  sub _tree_construction_main ($) { Line 6079  sub _tree_construction_main ($) {
6079                  my $i;                  my $i;
6080                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6081                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6082                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
                        tbody => 1, thead => 1, tfoot => 1,  
                       }->{$node->[1]}) {  
6083                      !!!cp ('t237');                      !!!cp ('t237');
6084                      $i = $_;                      $i = $_;
6085                      last INSCOPE;                      last INSCOPE;
6086                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6087                      !!!cp ('t238');                      !!!cp ('t238');
6088                      last INSCOPE;                      last INSCOPE;
6089                    }                    }
6090                  } # INSCOPE                  } # INSCOPE
6091                  unless (defined $i) {                  unless (defined $i) {
6092                    !!!cp ('t239');                    !!!cp ('t239');
6093                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!parse-error (type => 'unmatched end tag',
6094                                      text => $token->{tag_name}, token => $token);
6095                    ## Ignore the token                    ## Ignore the token
6096                      !!!nack ('t239.1');
6097                    !!!next-token;                    !!!next-token;
6098                    redo B;                    next B;
6099                  }                  }
6100                                    
6101                  ## Clear back to table body context                  ## Clear back to table body context
6102                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6103                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6104                    !!!cp ('t240');                    !!!cp ('t240');
6105                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6106                  }                  }
# Line 4686  sub _tree_construction_main ($) { Line 6126  sub _tree_construction_main ($) {
6126                my $i;                my $i;
6127                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6128                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6129                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6130                    !!!cp ('t241');                    !!!cp ('t241');
6131                    $i = $_;                    $i = $_;
6132                    last INSCOPE;                    last INSCOPE;
6133                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6134                    !!!cp ('t242');                    !!!cp ('t242');
6135                    last INSCOPE;                    last INSCOPE;
6136                  }                  }
6137                } # INSCOPE                } # INSCOPE
6138                unless (defined $i) {                unless (defined $i) {
6139                  !!!cp ('t243');                  !!!cp ('t243');
6140                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6141                                    text => $token->{tag_name}, token => $token);
6142                  ## Ignore the token                  ## Ignore the token
6143                    !!!nack ('t243.1');
6144                  !!!next-token;                  !!!next-token;
6145                  redo B;                  next B;
6146                }                }
6147                                    
6148                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 4711  sub _tree_construction_main ($) { Line 6151  sub _tree_construction_main ($) {
6151                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6152                                
6153                !!!next-token;                !!!next-token;
6154                redo B;                next B;
6155              } elsif ({              } elsif ({
6156                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6157                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4721  sub _tree_construction_main ($) { Line 6161  sub _tree_construction_main ($) {
6161                  my $i;                  my $i;
6162                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6163                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6164                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6165                      !!!cp ('t247');                      !!!cp ('t247');
6166                      $i = $_;                      $i = $_;
6167                      last INSCOPE;                      last INSCOPE;
6168                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6169                      !!!cp ('t248');                      !!!cp ('t248');
6170                      last INSCOPE;                      last INSCOPE;
6171                    }                    }
6172                  } # INSCOPE                  } # INSCOPE
6173                    unless (defined $i) {                    unless (defined $i) {
6174                      !!!cp ('t249');                      !!!cp ('t249');
6175                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!parse-error (type => 'unmatched end tag',
6176                                        text => $token->{tag_name}, token => $token);
6177                      ## Ignore the token                      ## Ignore the token
6178                        !!!nack ('t249.1');
6179                      !!!next-token;                      !!!next-token;
6180                      redo B;                      next B;
6181                    }                    }
6182                                    
6183                  ## As if </tr>                  ## As if </tr>
# Line 4745  sub _tree_construction_main ($) { Line 6185  sub _tree_construction_main ($) {
6185                  my $i;                  my $i;
6186                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6187                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6188                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6189                      !!!cp ('t250');                      !!!cp ('t250');
6190                      $i = $_;                      $i = $_;
6191                      last INSCOPE;                      last INSCOPE;
6192                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
6193                      !!!cp ('t251');                      !!!cp ('t251');
6194                      last INSCOPE;                      last INSCOPE;
6195                    }                    }
6196                  } # INSCOPE                  } # INSCOPE
6197                    unless (defined $i) {                    unless (defined $i) {
6198                      !!!cp ('t252');                      !!!cp ('t252');
6199                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!parse-error (type => 'unmatched end tag',
6200                                        text => 'tr', token => $token);
6201                      ## Ignore the token                      ## Ignore the token
6202                        !!!nack ('t252.1');
6203                      !!!next-token;                      !!!next-token;
6204                      redo B;                      next B;
6205                    }                    }
6206                                    
6207                  ## Clear back to table row context                  ## Clear back to table row context
6208                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6209                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
                 }->{$self->{open_elements}->[-1]->[1]}) {  
6210                    !!!cp ('t253');                    !!!cp ('t253');
6211  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6212                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
# Line 4782  sub _tree_construction_main ($) { Line 6221  sub _tree_construction_main ($) {
6221                my $i;                my $i;
6222                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6223                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6224                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6225                    !!!cp ('t254');                    !!!cp ('t254');
6226                    $i = $_;                    $i = $_;
6227                    last INSCOPE;                    last INSCOPE;
6228                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6229                    !!!cp ('t255');                    !!!cp ('t255');
6230                    last INSCOPE;                    last INSCOPE;
6231                  }                  }
6232                } # INSCOPE                } # INSCOPE
6233                unless (defined $i) {                unless (defined $i) {
6234                  !!!cp ('t256');                  !!!cp ('t256');
6235                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
6236                                    text => $token->{tag_name}, token => $token);
6237                  ## Ignore the token                  ## Ignore the token
6238                    !!!nack ('t256.1');
6239                  !!!next-token;                  !!!next-token;
6240                  redo B;                  next B;
6241                }                }
6242    
6243                ## Clear back to table body context                ## Clear back to table body context
6244                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6245                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
               }->{$self->{open_elements}->[-1]->[1]}) {  
6246                  !!!cp ('t257');                  !!!cp ('t257');
6247  ## ISSUE: Can this case be reached?  ## ISSUE: Can this case be reached?
6248                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
# Line 4812  sub _tree_construction_main ($) { Line 6250  sub _tree_construction_main ($) {
6250    
6251                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6252                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6253                  !!!nack ('t257.1');
6254                !!!next-token;                !!!next-token;
6255                redo B;                next B;
6256              } elsif ({              } elsif ({
6257                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6258                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6259                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6260                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6261                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6262                !!!cp ('t258');            !!!cp ('t258');
6263                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6264                ## Ignore the token                            text => $token->{tag_name}, token => $token);
6265                !!!next-token;            ## Ignore the token
6266                redo B;            !!!nack ('t258.1');
6267               !!!next-token;
6268              next B;
6269          } else {          } else {
6270            !!!cp ('t259');            !!!cp ('t259');
6271            !!!parse-error (type => 'in table:/'.$token->{tag_name});            !!!parse-error (type => 'in table:/',
6272                              text => $token->{tag_name}, token => $token);
6273    
6274            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6275            #            #
6276          }          }
6277          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6278            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6279                    @{$self->{open_elements}} == 1) { # redundant, maybe
6280              !!!parse-error (type => 'in body:#eof', token => $token);
6281              !!!cp ('t259.1');
6282              #
6283            } else {
6284              !!!cp ('t259.2');
6285              #
6286            }
6287    
6288            ## Stop parsing
6289            last B;
6290        } else {        } else {
6291          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6292        }        }
6293      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6294            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6295              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6296                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6297                unless (length $token->{data}) {                unless (length $token->{data}) {
6298                  !!!cp ('t260');                  !!!cp ('t260');
6299                  !!!next-token;                  !!!next-token;
6300                  redo B;                  next B;
6301                }                }
6302              }              }
6303                            
# Line 4851  sub _tree_construction_main ($) { Line 6306  sub _tree_construction_main ($) {
6306            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6307              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6308                !!!cp ('t262');                !!!cp ('t262');
6309                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6310                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6311                  !!!ack ('t262.1');
6312                !!!next-token;                !!!next-token;
6313                redo B;                next B;
6314              } else {              } else {
6315                !!!cp ('t263');                !!!cp ('t263');
6316                #                #
6317              }              }
6318            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6319              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6320                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6321                  !!!cp ('t264');                  !!!cp ('t264');
6322                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!parse-error (type => 'unmatched end tag',
6323                                    text => 'colgroup', token => $token);
6324                  ## Ignore the token                  ## Ignore the token
6325                  !!!next-token;                  !!!next-token;
6326                  redo B;                  next B;
6327                } else {                } else {
6328                  !!!cp ('t265');                  !!!cp ('t265');
6329                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6330                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6331                  !!!next-token;                  !!!next-token;
6332                  redo B;                              next B;            
6333                }                }
6334              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6335                !!!cp ('t266');                !!!cp ('t266');
6336                !!!parse-error (type => 'unmatched end tag:col');                !!!parse-error (type => 'unmatched end tag',
6337                                  text => 'col', token => $token);
6338                ## Ignore the token                ## Ignore the token
6339                !!!next-token;                !!!next-token;
6340                redo B;                next B;
6341              } else {              } else {
6342                !!!cp ('t267');                !!!cp ('t267');
6343                #                #
6344              }              }
6345            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6346              die "$0: $token->{type}: Unknown token type";          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6347            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6348              !!!cp ('t270.2');
6349              ## Stop parsing.
6350              last B;
6351            } else {
6352              ## NOTE: As if </colgroup>.
6353              !!!cp ('t270.1');
6354              pop @{$self->{open_elements}}; # colgroup
6355              $self->{insertion_mode} = IN_TABLE_IM;
6356              ## Reprocess.
6357              next B;
6358            }
6359          } else {
6360            die "$0: $token->{type}: Unknown token type";
6361          }
6362    
6363            ## As if </colgroup>            ## As if </colgroup>
6364            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6365              !!!cp ('t269');              !!!cp ('t269');
6366              !!!parse-error (type => 'unmatched end tag:colgroup');  ## TODO: Wrong error type?
6367                !!!parse-error (type => 'unmatched end tag',
6368                                text => 'colgroup', token => $token);
6369              ## Ignore the token              ## Ignore the token
6370                !!!nack ('t269.1');
6371              !!!next-token;              !!!next-token;
6372              redo B;              next B;
6373            } else {            } else {
6374              !!!cp ('t270');              !!!cp ('t270');
6375              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6376              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6377                !!!ack-later;
6378              ## reprocess              ## reprocess
6379              redo B;              next B;
6380            }            }
6381      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6382        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6383          !!!cp ('t271');          !!!cp ('t271');
6384          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6385          !!!next-token;          !!!next-token;
6386          redo B;          next B;
6387        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6388              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6389                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6390                  !!!cp ('t272');              !!!cp ('t272');
6391                  ## As if </option>              ## As if </option>
6392                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6393                } else {            } else {
6394                  !!!cp ('t273');              !!!cp ('t273');
6395                }            }
6396    
6397                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6398                !!!next-token;            !!!nack ('t273.1');
6399                redo B;            !!!next-token;
6400              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6401                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6402                  !!!cp ('t274');            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6403                  ## As if </option>              !!!cp ('t274');
6404                  pop @{$self->{open_elements}};              ## As if </option>
6405                } else {              pop @{$self->{open_elements}};
6406                  !!!cp ('t275');            } else {
6407                }              !!!cp ('t275');
6408              }
6409    
6410                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6411                  !!!cp ('t276');              !!!cp ('t276');
6412                  ## As if </optgroup>              ## As if </optgroup>
6413                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6414                } else {            } else {
6415                  !!!cp ('t277');              !!!cp ('t277');
6416                }            }
6417    
6418                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6419                !!!next-token;            !!!nack ('t277.1');
6420                redo B;            !!!next-token;
6421              } elsif ($token->{tag_name} eq 'select') {            next B;
6422  ## TODO: The type below is not good - <select> is replaced by </select>          } elsif ({
6423                !!!parse-error (type => 'not closed:select');                     select => 1, input => 1, textarea => 1,
6424                ## As if </select> instead                   }->{$token->{tag_name}} or
6425                ## have an element in table scope                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6426                my $i;                    {
6427                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                     caption => 1, table => 1,
6428                  my $node = $self->{open_elements}->[$_];                     tbody => 1, tfoot => 1, thead => 1,
6429                  if ($node->[1] eq $token->{tag_name}) {                     tr => 1, td => 1, th => 1,
6430                    !!!cp ('t278');                    }->{$token->{tag_name}})) {
6431                    $i = $_;            ## TODO: The type below is not good - <select> is replaced by </select>
6432                    last INSCOPE;            !!!parse-error (type => 'not closed', text => 'select',
6433                  } elsif ({                            token => $token);
6434                            table => 1, html => 1,            ## NOTE: As if the token were </select> (<select> case) or
6435                           }->{$node->[1]}) {            ## as if there were </select> (otherwise).
6436                    !!!cp ('t279');            ## have an element in table scope
6437                    last INSCOPE;            my $i;
6438                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6439                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6440                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6441                  !!!cp ('t280');                !!!cp ('t278');
6442                  !!!parse-error (type => 'unmatched end tag:select');                $i = $_;
6443                  ## Ignore the token                last INSCOPE;
6444                  !!!next-token;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6445                  redo B;                !!!cp ('t279');
6446                }                last INSCOPE;
6447                }
6448              } # INSCOPE
6449              unless (defined $i) {
6450                !!!cp ('t280');
6451                !!!parse-error (type => 'unmatched end tag',
6452                                text => 'select', token => $token);
6453                ## Ignore the token
6454                !!!nack ('t280.1');
6455                !!!next-token;
6456                next B;
6457              }
6458                                
6459                !!!cp ('t281');            !!!cp ('t281');
6460                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6461    
6462                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6463    
6464                !!!next-token;            if ($token->{tag_name} eq 'select') {
6465                redo B;              !!!nack ('t281.2');
6466                !!!next-token;
6467                next B;
6468              } else {
6469                !!!cp ('t281.1');
6470                !!!ack-later;
6471                ## Reprocess the token.
6472                next B;
6473              }
6474          } else {          } else {
6475            !!!cp ('t282');            !!!cp ('t282');
6476            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!parse-error (type => 'in select',
6477                              text => $token->{tag_name}, token => $token);
6478            ## Ignore the token            ## Ignore the token
6479              !!!nack ('t282.1');
6480            !!!next-token;            !!!next-token;
6481            redo B;            next B;
6482          }          }
6483        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6484              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6485                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6486                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6487                  !!!cp ('t283');              !!!cp ('t283');
6488                  ## As if </option>              ## As if </option>
6489                  splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
6490                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6491                  !!!cp ('t284');              !!!cp ('t284');
6492                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6493                } else {            } else {
6494                  !!!cp ('t285');              !!!cp ('t285');
6495                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6496                  ## Ignore the token                              text => $token->{tag_name}, token => $token);
6497                }              ## Ignore the token
6498                !!!next-token;            }
6499                redo B;            !!!nack ('t285.1');
6500              } elsif ($token->{tag_name} eq 'option') {            !!!next-token;
6501                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6502                  !!!cp ('t286');          } elsif ($token->{tag_name} eq 'option') {
6503                  pop @{$self->{open_elements}};            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6504                } else {              !!!cp ('t286');
6505                  !!!cp ('t287');              pop @{$self->{open_elements}};
6506                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            } else {
6507                  ## Ignore the token              !!!cp ('t287');
6508                }              !!!parse-error (type => 'unmatched end tag',
6509                !!!next-token;                              text => $token->{tag_name}, token => $token);
6510                redo B;              ## Ignore the token
6511              } elsif ($token->{tag_name} eq 'select') {            }
6512                ## have an element in table scope            !!!nack ('t287.1');
6513                my $i;            !!!next-token;
6514                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            next B;
6515                  my $node = $self->{open_elements}->[$_];          } elsif ($token->{tag_name} eq 'select') {
6516                  if ($node->[1] eq $token->{tag_name}) {            ## have an element in table scope
6517                    !!!cp ('t288');            my $i;
6518                    $i = $_;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6519                    last INSCOPE;              my $node = $self->{open_elements}->[$_];
6520                  } elsif ({              if ($node->[1] & SELECT_EL) {
6521                            table => 1, html => 1,                !!!cp ('t288');
6522                           }->{$node->[1]}) {                $i = $_;
6523                    !!!cp ('t289');                last INSCOPE;
6524                    last INSCOPE;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6525                  }                !!!cp ('t289');
6526                } # INSCOPE                last INSCOPE;
6527                unless (defined $i) {              }
6528                  !!!cp ('t290');            } # INSCOPE
6529                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            unless (defined $i) {
6530                  ## Ignore the token              !!!cp ('t290');
6531                  !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6532                  redo B;                              text => $token->{tag_name}, token => $token);
6533                }              ## Ignore the token
6534                !!!nack ('t290.1');
6535                !!!next-token;
6536                next B;
6537              }
6538                                
6539                !!!cp ('t291');            !!!cp ('t291');
6540                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6541    
6542                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6543    
6544                !!!next-token;            !!!nack ('t291.1');
6545                redo B;            !!!next-token;
6546              } elsif ({            next B;
6547                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6548                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6549                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6550                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6551                     }->{$token->{tag_name}}) {
6552  ## TODO: The following is wrong?  ## TODO: The following is wrong?
6553                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
6554                              text => $token->{tag_name}, token => $token);
6555                                
6556                ## have an element in table scope            ## have an element in table scope
6557                my $i;            my $i;
6558                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6559                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6560                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6561                    !!!cp ('t292');                !!!cp ('t292');
6562                    $i = $_;                $i = $_;
6563                    last INSCOPE;                last INSCOPE;
6564                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6565                            table => 1, html => 1,                !!!cp ('t293');
6566                           }->{$node->[1]}) {                last INSCOPE;
6567                    !!!cp ('t293');              }
6568                    last INSCOPE;            } # INSCOPE
6569                  }            unless (defined $i) {
6570                } # INSCOPE              !!!cp ('t294');
6571                unless (defined $i) {              ## Ignore the token
6572                  !!!cp ('t294');              !!!nack ('t294.1');
6573                  ## Ignore the token              !!!next-token;
6574                  !!!next-token;              next B;
6575                  redo B;            }
               }  
6576                                
6577                ## As if </select>            ## As if </select>
6578                ## have an element in table scope            ## have an element in table scope
6579                undef $i;            undef $i;
6580                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6581                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6582                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6583                    !!!cp ('t295');                !!!cp ('t295');
6584                    $i = $_;                $i = $_;
6585                    last INSCOPE;                last INSCOPE;
6586                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
6587  ## ISSUE: Can this state be reached?  ## ISSUE: Can this state be reached?
6588                    !!!cp ('t296');                !!!cp ('t296');
6589                    last INSCOPE;                last INSCOPE;
6590                  }              }
6591                } # INSCOPE            } # INSCOPE
6592                unless (defined $i) {            unless (defined $i) {
6593                  !!!cp ('t297');              !!!cp ('t297');
6594  ## TODO: The following error type is correct?  ## TODO: The following error type is correct?
6595                  !!!parse-error (type => 'unmatched end tag:select');              !!!parse-error (type => 'unmatched end tag',
6596                  ## Ignore the </select> token                              text => 'select', token => $token);
6597                  !!!next-token; ## TODO: ok?              ## Ignore the </select> token
6598                  redo B;              !!!nack ('t297.1');
6599                }              !!!next-token; ## TODO: ok?
6600                next B;
6601              }
6602                                
6603                !!!cp ('t298');            !!!cp ('t298');
6604                splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
6605    
6606                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6607    
6608                ## reprocess            !!!ack-later;
6609                redo B;            ## reprocess
6610              next B;
6611          } else {          } else {
6612            !!!cp ('t299');            !!!cp ('t299');
6613            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!parse-error (type => 'in select:/',
6614                              text => $token->{tag_name}, token => $token);
6615            ## Ignore the token            ## Ignore the token
6616              !!!nack ('t299.3');
6617            !!!next-token;            !!!next-token;
6618            redo B;            next B;
6619            }
6620          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6621            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6622                    @{$self->{open_elements}} == 1) { # redundant, maybe
6623              !!!cp ('t299.1');
6624              !!!parse-error (type => 'in body:#eof', token => $token);
6625            } else {
6626              !!!cp ('t299.2');
6627          }          }
6628    
6629            ## Stop parsing.
6630            last B;
6631        } else {        } else {
6632          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6633        }        }
6634      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6635        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6636          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6637            my $data = $1;            my $data = $1;
6638            ## As if in body            ## As if in body
6639            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 5125  sub _tree_construction_main ($) { Line 6643  sub _tree_construction_main ($) {
6643            unless (length $token->{data}) {            unless (length $token->{data}) {
6644              !!!cp ('t300');              !!!cp ('t300');
6645              !!!next-token;              !!!next-token;
6646              redo B;              next B;
6647            }            }
6648          }          }
6649                    
6650          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6651            !!!cp ('t301');            !!!cp ('t301');
6652            !!!parse-error (type => 'after html:#character');            !!!parse-error (type => 'after html:#text', token => $token);
6653              #
           ## Reprocess in the "after body" insertion mode.  
6654          } else {          } else {
6655            !!!cp ('t302');            !!!cp ('t302');
6656              ## "after body" insertion mode
6657              !!!parse-error (type => 'after body:#text', token => $token);
6658              #
6659          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6660    
6661          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6662          ## reprocess          ## reprocess
6663          redo B;          next B;
6664        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6665          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6666            !!!cp ('t303');            !!!cp ('t303');
6667            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!parse-error (type => 'after html',
6668                                        text => $token->{tag_name}, token => $token);
6669            ## Reprocess in the "after body" insertion mode.            #
6670          } else {          } else {
6671            !!!cp ('t304');            !!!cp ('t304');
6672              ## "after body" insertion mode
6673              !!!parse-error (type => 'after body',
6674                              text => $token->{tag_name}, token => $token);
6675              #
6676          }          }
6677    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6678          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6679            !!!ack-later;
6680          ## reprocess          ## reprocess
6681          redo B;          next B;
6682        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6683          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6684            !!!cp ('t305');            !!!cp ('t305');
6685            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!parse-error (type => 'after html:/',
6686                              text => $token->{tag_name}, token => $token);
6687                        
6688            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6689            ## Reprocess in the "after body" insertion mode.            ## Reprocess.
6690              next B;
6691          } else {          } else {
6692            !!!cp ('t306');            !!!cp ('t306');
6693          }          }
# Line 5175  sub _tree_construction_main ($) { Line 6696  sub _tree_construction_main ($) {
6696          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6697            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6698              !!!cp ('t307');              !!!cp ('t307');
6699              !!!parse-error (type => 'unmatched end tag:html');              !!!parse-error (type => 'unmatched end tag',
6700                                text => 'html', token => $token);
6701              ## Ignore the token              ## Ignore the token
6702              !!!next-token;              !!!next-token;
6703              redo B;              next B;
6704            } else {            } else {
6705              !!!cp ('t308');              !!!cp ('t308');
6706              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6707              !!!next-token;              !!!next-token;
6708              redo B;              next B;
6709            }            }
6710          } else {          } else {
6711            !!!cp ('t309');            !!!cp ('t309');
6712            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!parse-error (type => 'after body:/',
6713                              text => $token->{tag_name}, token => $token);
6714    
6715            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6716            ## reprocess            ## reprocess
6717            redo B;            next B;
6718          }          }
6719          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6720            !!!cp ('t309.2');
6721            ## Stop parsing
6722            last B;
6723        } else {        } else {
6724          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6725        }        }
6726      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6727        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6728          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6729            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6730                        
6731            unless (length $token->{data}) {            unless (length $token->{data}) {
6732              !!!cp ('t310');              !!!cp ('t310');
6733              !!!next-token;              !!!next-token;
6734              redo B;              next B;
6735            }            }
6736          }          }
6737                    
6738          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6739            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6740              !!!cp ('t311');              !!!cp ('t311');
6741              !!!parse-error (type => 'in frameset:#character');              !!!parse-error (type => 'in frameset:#text', token => $token);
6742            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6743              !!!cp ('t312');              !!!cp ('t312');
6744              !!!parse-error (type => 'after frameset:#character');              !!!parse-error (type => 'after frameset:#text', token => $token);
6745            } else { # "after html frameset"            } else { # "after after frameset"
6746              !!!cp ('t313');              !!!cp ('t313');
6747              !!!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');  
6748            }            }
6749                        
6750            ## Ignore the token.            ## Ignore the token.
# Line 5232  sub _tree_construction_main ($) { Line 6755  sub _tree_construction_main ($) {
6755              !!!cp ('t315');              !!!cp ('t315');
6756              !!!next-token;              !!!next-token;
6757            }            }
6758            redo B;            next B;
6759          }          }
6760                    
6761          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6762        } 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');  
         }  
   
6763          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6764              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6765            !!!cp ('t318');            !!!cp ('t318');
6766            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6767              !!!nack ('t318.1');
6768            !!!next-token;            !!!next-token;
6769            redo B;            next B;
6770          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6771                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6772            !!!cp ('t319');            !!!cp ('t319');
6773            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6774            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6775              !!!ack ('t319.1');
6776            !!!next-token;            !!!next-token;
6777            redo B;            next B;
6778          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6779            !!!cp ('t320');            !!!cp ('t320');
6780            ## NOTE: As if in body.            ## NOTE: As if in head.
6781            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
6782            redo B;            next B;
6783    
6784              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6785              ## has no parse error.
6786          } else {          } else {
6787            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6788              !!!cp ('t321');              !!!cp ('t321');
6789              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!parse-error (type => 'in frameset',
6790            } else {                              text => $token->{tag_name}, token => $token);
6791              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6792              !!!cp ('t322');              !!!cp ('t322');
6793              !!!parse-error (type => 'after frameset:'.$token->{tag_name});              !!!parse-error (type => 'after frameset',
6794                                text => $token->{tag_name}, token => $token);
6795              } else { # "after after frameset"
6796                !!!cp ('t322.2');
6797                !!!parse-error (type => 'after after frameset',
6798                                text => $token->{tag_name}, token => $token);
6799            }            }
6800            ## Ignore the token            ## Ignore the token
6801              !!!nack ('t322.1');
6802            !!!next-token;            !!!next-token;
6803            redo B;            next B;
6804          }          }
6805        } 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');  
         }  
   
6806          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6807              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6808            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6809                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6810              !!!cp ('t325');              !!!cp ('t325');
6811              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
6812                                text => $token->{tag_name}, token => $token);
6813              ## Ignore the token              ## Ignore the token
6814              !!!next-token;              !!!next-token;
6815            } else {            } else {
# Line 5303  sub _tree_construction_main ($) { Line 6819  sub _tree_construction_main ($) {
6819            }            }
6820    
6821            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6822                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6823              !!!cp ('t327');              !!!cp ('t327');
6824              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6825            } else {            } else {
6826              !!!cp ('t328');              !!!cp ('t328');
6827            }            }
6828            redo B;            next B;
6829          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6830                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6831            !!!cp ('t329');            !!!cp ('t329');
6832            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6833            !!!next-token;            !!!next-token;
6834            redo B;            next B;
6835          } else {          } else {
6836            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6837              !!!cp ('t330');              !!!cp ('t330');
6838              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!parse-error (type => 'in frameset:/',
6839            } else {                              text => $token->{tag_name}, token => $token);
6840              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6841                !!!cp ('t330.1');
6842                !!!parse-error (type => 'after frameset:/',
6843                                text => $token->{tag_name}, token => $token);
6844              } else { # "after after html"
6845              !!!cp ('t331');              !!!cp ('t331');
6846              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});              !!!parse-error (type => 'after after frameset:/',
6847                                text => $token->{tag_name}, token => $token);
6848            }            }
6849            ## Ignore the token            ## Ignore the token
6850            !!!next-token;            !!!next-token;
6851            redo B;            next B;
6852            }
6853          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6854            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6855                    @{$self->{open_elements}} == 1) { # redundant, maybe
6856              !!!cp ('t331.1');
6857              !!!parse-error (type => 'in body:#eof', token => $token);
6858            } else {
6859              !!!cp ('t331.2');
6860          }          }
6861            
6862            ## Stop parsing
6863            last B;
6864        } else {        } else {
6865          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6866        }        }
   
       ## ISSUE: An issue in spec here  
6867      } else {      } else {
6868        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6869      }      }
# Line 5343  sub _tree_construction_main ($) { Line 6874  sub _tree_construction_main ($) {
6874          !!!cp ('t332');          !!!cp ('t332');
6875          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6876          $script_start_tag->();          $script_start_tag->();
6877          redo B;          next B;
6878        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6879          !!!cp ('t333');          !!!cp ('t333');
6880          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6881          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6882          redo B;          next B;
6883        } elsif ({        } elsif ({
6884                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6885                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6886          !!!cp ('t334');          !!!cp ('t334');
6887          ## 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
6888          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6889          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6890            !!!ack ('t334.1');
6891          !!!next-token;          !!!next-token;
6892          redo B;          next B;
6893        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6894          ## 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
6895          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6896          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6897    
6898          unless ($self->{confident}) {          unless ($self->{confident}) {
6899            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6900              !!!cp ('t335');              !!!cp ('t335');
6901                ## NOTE: Whether the encoding is supported or not is handled
6902                ## in the {change_encoding} callback.
6903              $self->{change_encoding}              $self->{change_encoding}
6904                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6905                            
6906              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6907                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6908                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6909                                           ->{has_reference});                                           ->{has_reference});
6910            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6911              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6912                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6913                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6914                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6915                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6916                       /x) {
6917                !!!cp ('t336');                !!!cp ('t336');
6918                  ## NOTE: Whether the encoding is supported or not is handled
6919                  ## in the {change_encoding} callback.
6920                $self->{change_encoding}                $self->{change_encoding}
6921                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6922                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6923                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6924                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 5406  sub _tree_construction_main ($) { Line 6942  sub _tree_construction_main ($) {
6942            }            }
6943          }          }
6944    
6945            !!!ack ('t338.1');
6946          !!!next-token;          !!!next-token;
6947          redo B;          next B;
6948        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6949          !!!cp ('t341');          !!!cp ('t341');
6950          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6951          $parse_rcdata->(RCDATA_CONTENT_MODEL);          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6952          redo B;          next B;
6953        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6954          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6955                                
6956          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6957              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6958            !!!cp ('t342');            !!!cp ('t342');
6959            ## Ignore the token            ## Ignore the token
6960          } else {          } else {
# Line 5431  sub _tree_construction_main ($) { Line 6968  sub _tree_construction_main ($) {
6968              }              }
6969            }            }
6970          }          }
6971            !!!nack ('t343.1');
6972          !!!next-token;          !!!next-token;
6973          redo B;          next B;
6974        } elsif ({        } elsif ({
6975                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6976                  div => 1, dl => 1, fieldset => 1,  
6977                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
6978                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6979                    center => 1, datagrid => 1, details => 1, dialog => 1,
6980                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6981                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6982                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6983                    section => 1, ul => 1,
6984                    ## NOTE: As normal, but drops leading newline
6985                  pre => 1, listing => 1,                  pre => 1, listing => 1,
6986                    ## NOTE: As normal, but interacts with the form element pointer
6987                    form => 1,
6988                    
6989                    table => 1,
6990                    hr => 1,
6991                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6992            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6993              !!!cp ('t350');
6994              !!!parse-error (type => 'in form:form', token => $token);
6995              ## Ignore the token
6996              !!!nack ('t350.1');
6997              !!!next-token;
6998              next B;
6999            }
7000    
7001          ## has a p element in scope          ## has a p element in scope
7002          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7003            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7004              !!!cp ('t344');              !!!cp ('t344');
7005              !!!back-token;              !!!back-token; # <form>
7006              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7007              redo B;                        line => $token->{line}, column => $token->{column}};
7008            } elsif ({              next B;
7009                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7010              !!!cp ('t345');              !!!cp ('t345');
7011              last INSCOPE;              last INSCOPE;
7012            }            }
7013          } # INSCOPE          } # INSCOPE
7014                        
7015          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7016          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7017              !!!nack ('t346.1');
7018            !!!next-token;            !!!next-token;
7019            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7020              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
# Line 5470  sub _tree_construction_main ($) { Line 7027  sub _tree_construction_main ($) {
7027            } else {            } else {
7028              !!!cp ('t348');              !!!cp ('t348');
7029            }            }
7030          } else {          } elsif ($token->{tag_name} eq 'form') {
7031            !!!cp ('t347');            !!!cp ('t347.1');
7032              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7033    
7034              !!!nack ('t347.2');
7035            !!!next-token;            !!!next-token;
7036          }          } elsif ($token->{tag_name} eq 'table') {
7037          redo B;            !!!cp ('t382');
7038        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7039          if (defined $self->{form_element}) {            
7040            !!!cp ('t350');            $self->{insertion_mode} = IN_TABLE_IM;
7041            !!!parse-error (type => 'in form:form');  
7042            ## Ignore the token            !!!nack ('t382.1');
7043              !!!next-token;
7044            } elsif ($token->{tag_name} eq 'hr') {
7045              !!!cp ('t386');
7046              pop @{$self->{open_elements}};
7047            
7048              !!!nack ('t386.1');
7049            !!!next-token;            !!!next-token;
           redo B;  
7050          } else {          } else {
7051            ## 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];  
7052            !!!next-token;            !!!next-token;
           redo B;  
7053          }          }
7054            next B;
7055        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7056          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7057          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7058            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7059              !!!cp ('t353');            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7060              !!!back-token;            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7061              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7062              redo B;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7063            } elsif ({            ## object (Fx)
7064                      table => 1, caption => 1, td => 1, th => 1,            ## Generate non-tree (non-conforming)
7065                      button => 1, marquee => 1, object => 1, html => 1,            ## basefont (IE7 (where basefont is non-void)), center (IE),
7066                     }->{$_->[1]}) {            ## form (IE), hn (IE)
7067              !!!cp ('t354');          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7068              last INSCOPE;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7069            }            ## div (Fx, S)
7070          } # INSCOPE  
7071                      my $non_optional;
         ## Step 1  
7072          my $i = -1;          my $i = -1;
7073          my $node = $self->{open_elements}->[$i];  
7074          LI: {          ## 1.
7075            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7076            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7077              if ($i != -1) {              ## 2. (a) As if </li>
7078                !!!cp ('t355');              {
7079                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7080                                $self->{open_elements}->[-1]->[1]);                #
7081              } else {  
7082                !!!cp ('t356');                ## Otherwise
7083    
7084                  ## 1. generate implied end tags, except for </li>
7085                  #
7086    
7087                  ## 2. If current node != "li", parse error
7088                  if ($non_optional) {
7089                    !!!parse-error (type => 'not closed',
7090                                    text => $non_optional->[0]->manakai_local_name,
7091                                    token => $token);
7092                    !!!cp ('t355');
7093                  } else {
7094                    !!!cp ('t356');
7095                  }
7096    
7097                  ## 3. Pop
7098                  splice @{$self->{open_elements}}, $i;
7099              }              }
7100              splice @{$self->{open_elements}}, $i;  
7101              last LI;              last; ## 2. (b) goto 5.
7102            } else {            } elsif (
7103                       ## NOTE: not "formatting" and not "phrasing"
7104                       ($node->[1] & SPECIAL_EL or
7105                        $node->[1] & SCOPING_EL) and
7106                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7107    
7108                       (not $node->[1] & ADDRESS_EL) &
7109                       (not $node->[1] & DIV_EL) &
7110                       (not $node->[1] & P_EL)) {
7111                ## 3.
7112              !!!cp ('t357');              !!!cp ('t357');
7113            }              last; ## goto 5.
7114                        } 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') {  
7115              !!!cp ('t358');              !!!cp ('t358');
7116              last LI;              #
7117              } else {
7118                !!!cp ('t359');
7119                $non_optional ||= $node;
7120                #
7121            }            }
7122                        ## 4.
7123            !!!cp ('t359');            ## goto 2.
           ## Step 4  
7124            $i--;            $i--;
7125            $node = $self->{open_elements}->[$i];          }
7126            redo LI;  
7127          } # 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  
7128          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7129            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7130              !!!cp ('t360');              !!!cp ('t353');
7131              !!!back-token;  
7132              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              ## NOTE: |<p><li>|, for example.
7133              redo B;  
7134            } elsif ({              !!!back-token; # <x>
7135                      table => 1, caption => 1, td => 1, th => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7136                      button => 1, marquee => 1, object => 1, html => 1,                        line => $token->{line}, column => $token->{column}};
7137                     }->{$_->[1]}) {              next B;
7138              !!!cp ('t361');            } elsif ($_->[1] & SCOPING_EL) {
7139                !!!cp ('t354');
7140              last INSCOPE;              last INSCOPE;
7141            }            }
7142          } # INSCOPE          } # INSCOPE
7143              
7144          ## Step 1          ## 5. (b) insert
7145            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7146            !!!nack ('t359.1');
7147            !!!next-token;
7148            next B;
7149          } elsif ($token->{tag_name} eq 'dt' or
7150                   $token->{tag_name} eq 'dd') {
7151            ## NOTE: As normal, but imply </dt> or </dd> when ...
7152    
7153            my $non_optional;
7154          my $i = -1;          my $i = -1;
7155          my $node = $self->{open_elements}->[$i];  
7156          LI: {          ## 1.
7157            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7158            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7159              if ($i != -1) {              ## 2. (a) As if </li>
7160                !!!cp ('t362');              {
7161                !!!parse-error (type => 'end tag missing:'.                ## If no </li> - not applied
7162                                $self->{open_elements}->[-1]->[1]);                #
7163              } else {  
7164                !!!cp ('t363');                ## Otherwise
7165    
7166                  ## 1. generate implied end tags, except for </dt> or </dd>
7167                  #
7168    
7169                  ## 2. If current node != "dt"|"dd", parse error
7170                  if ($non_optional) {
7171                    !!!parse-error (type => 'not closed',
7172                                    text => $non_optional->[0]->manakai_local_name,
7173                                    token => $token);
7174                    !!!cp ('t355.1');
7175                  } else {
7176                    !!!cp ('t356.1');
7177                  }
7178    
7179                  ## 3. Pop
7180                  splice @{$self->{open_elements}}, $i;
7181              }              }
7182              splice @{$self->{open_elements}}, $i;  
7183              last LI;              last; ## 2. (b) goto 5.
7184              } elsif (
7185                       ## NOTE: not "formatting" and not "phrasing"
7186                       ($node->[1] & SPECIAL_EL or
7187                        $node->[1] & SCOPING_EL) and
7188                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7189    
7190                       (not $node->[1] & ADDRESS_EL) &
7191                       (not $node->[1] & DIV_EL) &
7192                       (not $node->[1] & P_EL)) {
7193                ## 3.
7194                !!!cp ('t357.1');
7195                last; ## goto 5.
7196              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7197                !!!cp ('t358.1');
7198                #
7199            } else {            } else {
7200              !!!cp ('t364');              !!!cp ('t359.1');
7201            }              $non_optional ||= $node;
7202                          #
           ## 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;  
7203            }            }
7204                        ## 4.
7205            !!!cp ('t366');            ## goto 2.
           ## Step 4  
7206            $i--;            $i--;
7207            $node = $self->{open_elements}->[$i];          }
7208            redo LI;  
7209          } # LI          ## 5. (a) has a |p| element in scope
7210                      INSCOPE: for (reverse @{$self->{open_elements}}) {
7211          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            if ($_->[1] & P_EL) {
7212                !!!cp ('t353.1');
7213                !!!back-token; # <x>
7214                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7215                          line => $token->{line}, column => $token->{column}};
7216                next B;
7217              } elsif ($_->[1] & SCOPING_EL) {
7218                !!!cp ('t354.1');
7219                last INSCOPE;
7220              }
7221            } # INSCOPE
7222    
7223            ## 5. (b) insert
7224            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7225            !!!nack ('t359.2');
7226          !!!next-token;          !!!next-token;
7227          redo B;          next B;
7228        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
7229            ## NOTE: As normal, but effectively ends parsing
7230    
7231          ## has a p element in scope          ## has a p element in scope
7232          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7233            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7234              !!!cp ('t367');              !!!cp ('t367');
7235              !!!back-token;              !!!back-token; # <plaintext>
7236              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7237              redo B;                        line => $token->{line}, column => $token->{column}};
7238            } elsif ({              next B;
7239                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
7240              !!!cp ('t368');              !!!cp ('t368');
7241              last INSCOPE;              last INSCOPE;
7242            }            }
7243          } # INSCOPE          } # INSCOPE
7244                        
7245          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7246                        
7247          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7248                        
7249            !!!nack ('t368.1');
7250          !!!next-token;          !!!next-token;
7251          redo B;          next B;
7252        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7253          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7254            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7255            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7256              !!!cp ('t371');              !!!cp ('t371');
7257              !!!parse-error (type => 'in a:a');              !!!parse-error (type => 'in a:a', token => $token);
7258                            
7259              !!!back-token;              !!!back-token; # <a>
7260              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7261              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7262                $formatting_end_tag->($token);
7263                            
7264              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7265                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
# Line 5673  sub _tree_construction_main ($) { Line 7284  sub _tree_construction_main ($) {
7284                        
7285          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7286    
7287          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7288          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7289    
7290            !!!nack ('t374.1');
7291          !!!next-token;          !!!next-token;
7292          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;  
7293        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7294          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7295    
7296          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7297          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7298            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7299            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7300              !!!cp ('t376');              !!!cp ('t376');
7301              !!!parse-error (type => 'in nobr:nobr');              !!!parse-error (type => 'in nobr:nobr', token => $token);
7302              !!!back-token;              !!!back-token; # <nobr>
7303              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7304              redo B;                        line => $token->{line}, column => $token->{column}};
7305            } elsif ({              next B;
7306                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7307              !!!cp ('t377');              !!!cp ('t377');
7308              last INSCOPE;              last INSCOPE;
7309            }            }
7310          } # INSCOPE          } # INSCOPE
7311                    
7312          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7313          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7314                    
7315            !!!nack ('t377.1');
7316          !!!next-token;          !!!next-token;
7317          redo B;          next B;
7318        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7319          ## has a button element in scope          ## has a button element in scope
7320          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7321            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7322            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7323              !!!cp ('t378');              !!!cp ('t378');
7324              !!!parse-error (type => 'in button:button');              !!!parse-error (type => 'in button:button', token => $token);
7325              !!!back-token;              !!!back-token; # <button>
7326              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7327              redo B;                        line => $token->{line}, column => $token->{column}};
7328            } elsif ({              next B;
7329                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($node->[1] & SCOPING_EL) {
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7330              !!!cp ('t379');              !!!cp ('t379');
7331              last INSCOPE;              last INSCOPE;
7332            }            }
# Line 5738  sub _tree_construction_main ($) { Line 7334  sub _tree_construction_main ($) {
7334                        
7335          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7336                        
7337          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7338    
7339          ## TODO: associate with $self->{form_element} if defined          ## TODO: associate with $self->{form_element} if defined
7340    
7341          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7342    
7343            !!!nack ('t379.1');
7344          !!!next-token;          !!!next-token;
7345          redo B;          next B;
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         !!!cp ('t380');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         !!!cp ('t381');  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!cp ('t382');  
             !!!back-token;  
             $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
             redo B;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             !!!cp ('t383');  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];  
   
         $self->{insertion_mode} = IN_TABLE_IM;  
             
         !!!next-token;  
         redo B;  
7346        } elsif ({        } elsif ({
7347                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7348                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7349                  image => 1,                  noembed => 1,
7350                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7351                    noscript => 0, ## TODO: 1 if scripting is enabled
7352                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7353          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7354            !!!cp ('t384');            !!!cp ('t381');
7355            !!!parse-error (type => 'image');            $reconstruct_active_formatting_elements->($insert_to_current);
           $token->{tag_name} = 'img';  
7356          } else {          } else {
7357            !!!cp ('t385');            !!!cp ('t399');
7358          }          }
7359            ## NOTE: There is an "as if in body" code clone.
7360          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7361          $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;  
7362        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7363          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7364                    
7365          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7366            !!!cp ('t389');            !!!cp ('t389');
7367            ## Ignore the token            ## Ignore the token
7368              !!!nack ('t389'); ## NOTE: Not acknowledged.
7369            !!!next-token;            !!!next-token;
7370            redo B;            next B;
7371          } else {          } else {
7372              !!!ack ('t391.1');
7373    
7374            my $at = $token->{attributes};            my $at = $token->{attributes};
7375            my $form_attrs;            my $form_attrs;
7376            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5856  sub _tree_construction_main ($) { Line 7380  sub _tree_construction_main ($) {
7380            delete $at->{prompt};            delete $at->{prompt};
7381            my @tokens = (            my @tokens = (
7382                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7383                           attributes => $form_attrs},                           attributes => $form_attrs,
7384                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7385                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7386                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7387                            {type => START_TAG_TOKEN, tag_name => 'p',
7388                             line => $token->{line}, column => $token->{column}},
7389                            {type => START_TAG_TOKEN, tag_name => 'label',
7390                             line => $token->{line}, column => $token->{column}},
7391                         );                         );
7392            if ($prompt_attr) {            if ($prompt_attr) {
7393              !!!cp ('t390');              !!!cp ('t390');
7394              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7395                               #line => $token->{line}, column => $token->{column},
7396                              };
7397            } else {            } else {
7398              !!!cp ('t391');              !!!cp ('t391');
7399              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7400                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7401                               #line => $token->{line}, column => $token->{column},
7402                              }; # SHOULD
7403              ## TODO: make this configurable              ## TODO: make this configurable
7404            }            }
7405            push @tokens,            push @tokens,
7406                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7407                             line => $token->{line}, column => $token->{column}},
7408                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7409                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7410                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7411                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7412                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7413            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7414                             line => $token->{line}, column => $token->{column}},
7415                            {type => END_TAG_TOKEN, tag_name => 'form',
7416                             line => $token->{line}, column => $token->{column}};
7417            !!!back-token (@tokens);            !!!back-token (@tokens);
7418            redo B;            !!!next-token;
7419              next B;
7420          }          }
7421        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7422          my $tag_name = $token->{tag_name};          ## Step 1
7423          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
7424                    
7425            ## Step 2
7426          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7427    
7428            ## Step 3
7429            $self->{ignore_newline} = 1;
7430    
7431            ## Step 4
7432            ## ISSUE: This step is wrong. (r2302 enbugged)
7433    
7434            ## Step 5
7435          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7436          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7437            
7438          $insert->($el);          ## Step 6-7
7439                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7440          my $text = '';  
7441            !!!nack ('t392.1');
7442          !!!next-token;          !!!next-token;
7443          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7444            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7445            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7446              !!!cp ('t392');          ## has an |option| element in scope
7447              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7448            } else {            my $node = $self->{open_elements}->[$_];
7449              !!!cp ('t393');            if ($node->[1] & OPTION_EL) {
7450                !!!cp ('t397.1');
7451                ## NOTE: As if </option>
7452                !!!back-token; # <option> or <optgroup>
7453                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7454                          line => $token->{line}, column => $token->{column}};
7455                next B;
7456              } elsif ($node->[1] & SCOPING_EL) {
7457                !!!cp ('t397.2');
7458                last INSCOPE;
7459            }            }
7460          } else {          } # INSCOPE
7461            !!!cp ('t394');  
7462          }          $reconstruct_active_formatting_elements->($insert_to_current);
7463          while ($token->{type} == CHARACTER_TOKEN) {  
7464            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7465            $text .= $token->{data};  
7466            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
7467          !!!next-token;          !!!next-token;
7468          redo B;          redo B;
7469        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7470                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7471                  noembed => 1,          ## has a |ruby| element in scope
7472                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7473                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7474                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7475          !!!cp ('t399');              !!!cp ('t398.1');
7476          ## NOTE: There is an "as if in body" code clone.              ## generate implied end tags
7477          $parse_rcdata->(CDATA_CONTENT_MODEL);              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7478                  !!!cp ('t398.2');
7479                  pop @{$self->{open_elements}};
7480                }
7481                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7482                  !!!cp ('t398.3');
7483                  !!!parse-error (type => 'not closed',
7484                                  text => $self->{open_elements}->[-1]->[0]
7485                                      ->manakai_local_name,
7486                                  token => $token);
7487                  pop @{$self->{open_elements}}
7488                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7489                }
7490                last INSCOPE;
7491              } elsif ($node->[1] & SCOPING_EL) {
7492                !!!cp ('t398.4');
7493                last INSCOPE;
7494              }
7495            } # INSCOPE
7496    
7497            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7498    
7499            !!!nack ('t398.5');
7500            !!!next-token;
7501          redo B;          redo B;
7502        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7503          !!!cp ('t400');                 $token->{tag_name} eq 'svg') {
7504          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7505    
7506          ## TODO: associate with $self->{form_element} if defined          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7507    
7508            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7509    
7510            ## "adjust foreign attributes" - done in insert-element-f
7511                    
7512          $self->{insertion_mode} = IN_SELECT_IM;          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7513            
7514            if ($self->{self_closing}) {
7515              pop @{$self->{open_elements}};
7516              !!!ack ('t398.6');
7517            } else {
7518              !!!cp ('t398.7');
7519              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7520              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7521              ## mode, "in body" (not "in foreign content") secondary insertion
7522              ## mode, maybe.
7523            }
7524    
7525          !!!next-token;          !!!next-token;
7526          redo B;          next B;
7527        } elsif ({        } elsif ({
7528                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7529                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7530                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7531                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7532                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7533          !!!cp ('t401');          !!!cp ('t401');
7534          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'in body',
7535                            text => $token->{tag_name}, token => $token);
7536          ## Ignore the token          ## Ignore the token
7537            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7538            !!!next-token;
7539            next B;
7540          } elsif ($token->{tag_name} eq 'param' or
7541                   $token->{tag_name} eq 'source') {
7542            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7543            pop @{$self->{open_elements}};
7544    
7545            !!!ack ('t398.5');
7546          !!!next-token;          !!!next-token;
7547          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7548        } else {        } else {
7549          !!!cp ('t402');          if ($token->{tag_name} eq 'image') {
7550              !!!cp ('t384');
7551              !!!parse-error (type => 'image', token => $token);
7552              $token->{tag_name} = 'img';
7553            } else {
7554              !!!cp ('t385');
7555            }
7556    
7557            ## NOTE: There is an "as if <br>" code clone.
7558          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7559                    
7560          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7561    
7562            if ({
7563                 applet => 1, marquee => 1, object => 1,
7564                }->{$token->{tag_name}}) {
7565              !!!cp ('t380');
7566              push @$active_formatting_elements, ['#marker', ''];
7567              !!!nack ('t380.1');
7568            } elsif ({
7569                      b => 1, big => 1, em => 1, font => 1, i => 1,
7570                      s => 1, small => 1, strike => 1,
7571                      strong => 1, tt => 1, u => 1,
7572                     }->{$token->{tag_name}}) {
7573              !!!cp ('t375');
7574              push @$active_formatting_elements, $self->{open_elements}->[-1];
7575              !!!nack ('t375.1');
7576            } elsif ($token->{tag_name} eq 'input') {
7577              !!!cp ('t388');
7578              ## TODO: associate with $self->{form_element} if defined
7579              pop @{$self->{open_elements}};
7580              !!!ack ('t388.2');
7581            } elsif ({
7582                      area => 1, basefont => 1, bgsound => 1, br => 1,
7583                      embed => 1, img => 1, spacer => 1, wbr => 1,
7584                     }->{$token->{tag_name}}) {
7585              !!!cp ('t388.1');
7586              pop @{$self->{open_elements}};
7587              !!!ack ('t388.3');
7588            } elsif ($token->{tag_name} eq 'select') {
7589              ## TODO: associate with $self->{form_element} if defined
7590            
7591              if ($self->{insertion_mode} & TABLE_IMS or
7592                  $self->{insertion_mode} & BODY_TABLE_IMS or
7593                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7594                !!!cp ('t400.1');
7595                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7596              } else {
7597                !!!cp ('t400.2');
7598                $self->{insertion_mode} = IN_SELECT_IM;
7599              }
7600              !!!nack ('t400.3');
7601            } else {
7602              !!!nack ('t402');
7603            }
7604                    
7605          !!!next-token;          !!!next-token;
7606          redo B;          next B;
7607        }        }
7608      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7609        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7610          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7611              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7612            for (@{$self->{open_elements}}) {          INSCOPE: {
7613              unless ({            for (reverse @{$self->{open_elements}}) {
7614                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7615                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7616                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7617                      }->{$_->[1]}) {                last INSCOPE;
7618                !!!cp ('t403');              } elsif ($_->[1] & SCOPING_EL) {
7619                !!!parse-error (type => 'not closed:'.$_->[1]);                !!!cp ('t405.1');
7620              } else {                last;
               !!!cp ('t404');  
7621              }              }
7622            }            }
7623    
7624            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7625            !!!next-token;  
7626            redo B;            !!!parse-error (type => 'unmatched end tag',
7627          } else {                            text => $token->{tag_name}, token => $token);
7628            !!!cp ('t405');            ## NOTE: Ignore the token.
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
7629            !!!next-token;            !!!next-token;
7630            redo B;            next B;
7631            } # INSCOPE
7632    
7633            for (@{$self->{open_elements}}) {
7634              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7635                !!!cp ('t403');
7636                !!!parse-error (type => 'not closed',
7637                                text => $_->[0]->manakai_local_name,
7638                                token => $token);
7639                last;
7640              } else {
7641                !!!cp ('t404');
7642              }
7643          }          }
7644    
7645            $self->{insertion_mode} = AFTER_BODY_IM;
7646            !!!next-token;
7647            next B;
7648        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7649          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
7650            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7651            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7652                $self->{open_elements}->[1]->[1] & BODY_EL) {
7653              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7654              !!!cp ('t406');              !!!cp ('t406');
7655              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              !!!parse-error (type => 'not closed',
7656                                text => $self->{open_elements}->[1]->[0]
7657                                    ->manakai_local_name,
7658                                token => $token);
7659            } else {            } else {
7660              !!!cp ('t407');              !!!cp ('t407');
7661            }            }
7662            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7663            ## reprocess            ## reprocess
7664            redo B;            next B;
7665          } else {          } else {
7666            !!!cp ('t408');            !!!cp ('t408');
7667            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7668                              text => $token->{tag_name}, token => $token);
7669            ## Ignore the token            ## Ignore the token
7670            !!!next-token;            !!!next-token;
7671            redo B;            next B;
7672          }          }
7673        } elsif ({        } elsif ({
7674                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7675                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7676                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7677                    address => 1, article => 1, aside => 1, blockquote => 1,
7678                    center => 1, datagrid => 1, details => 1, dialog => 1,
7679                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7680                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7681                    ol => 1, pre => 1, section => 1, ul => 1,
7682    
7683                    ## NOTE: As normal, but ... optional tags
7684                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7685                  button => 1, marquee => 1, object => 1,  
7686                    applet => 1, button => 1, marquee => 1, object => 1,
7687                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7688            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7689            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7690            ## </dd>" code.
7691    
7692          ## has an element in scope          ## has an element in scope
7693          my $i;          my $i;
7694          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7695            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7696            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7697              !!!cp ('t410');              !!!cp ('t410');
7698              $i = $_;              $i = $_;
7699              last INSCOPE;              last INSCOPE;
7700            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7701              !!!cp ('t411');              !!!cp ('t411');
7702              last INSCOPE;              last INSCOPE;
7703            }            }
# Line 6042  sub _tree_construction_main ($) { Line 7705  sub _tree_construction_main ($) {
7705    
7706          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7707            !!!cp ('t413');            !!!cp ('t413');
7708            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7709                              text => $token->{tag_name}, token => $token);
7710              ## NOTE: Ignore the token.
7711          } else {          } else {
7712            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7713            while ({            while ({
7714                      ## END_TAG_OPTIONAL_EL
7715                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
7716                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
7717                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
7718                      option => 1,
7719                      optgroup => 1,
7720                    p => 1,                    p => 1,
7721                   }->{$self->{open_elements}->[-1]->[1]}) {                    rt => 1,
7722                      rp => 1,
7723                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7724              !!!cp ('t409');              !!!cp ('t409');
7725              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7726            }            }
7727    
7728            ## Step 2.            ## Step 2.
7729            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7730                      ne $token->{tag_name}) {
7731              !!!cp ('t412');              !!!cp ('t412');
7732              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7733                                text => $self->{open_elements}->[-1]->[0]
7734                                    ->manakai_local_name,
7735                                token => $token);
7736            } else {            } else {
7737              !!!cp ('t414');              !!!cp ('t414');
7738            }            }
# Line 6069  sub _tree_construction_main ($) { Line 7743  sub _tree_construction_main ($) {
7743            ## Step 4.            ## Step 4.
7744            $clear_up_to_marker->()            $clear_up_to_marker->()
7745                if {                if {
7746                  button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
7747                }->{$token->{tag_name}};                }->{$token->{tag_name}};
7748          }          }
7749          !!!next-token;          !!!next-token;
7750          redo B;          next B;
7751        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7752            ## NOTE: As normal, but interacts with the form element pointer
7753    
7754          undef $self->{form_element};          undef $self->{form_element};
7755    
7756          ## has an element in scope          ## has an element in scope
7757          my $i;          my $i;
7758          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7759            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7760            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7761              !!!cp ('t418');              !!!cp ('t418');
7762              $i = $_;              $i = $_;
7763              last INSCOPE;              last INSCOPE;
7764            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7765              !!!cp ('t419');              !!!cp ('t419');
7766              last INSCOPE;              last INSCOPE;
7767            }            }
# Line 6096  sub _tree_construction_main ($) { Line 7769  sub _tree_construction_main ($) {
7769    
7770          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7771            !!!cp ('t421');            !!!cp ('t421');
7772            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7773                              text => $token->{tag_name}, token => $token);
7774              ## NOTE: Ignore the token.
7775          } else {          } else {
7776            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7777            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7778              !!!cp ('t417');              !!!cp ('t417');
7779              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7780            }            }
7781                        
7782            ## Step 2.            ## Step 2.
7783            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7784                      ne $token->{tag_name}) {
7785              !!!cp ('t417.1');              !!!cp ('t417.1');
7786              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7787                                text => $self->{open_elements}->[-1]->[0]
7788                                    ->manakai_local_name,
7789                                token => $token);
7790            } else {            } else {
7791              !!!cp ('t420');              !!!cp ('t420');
7792            }              }  
# Line 6119  sub _tree_construction_main ($) { Line 7796  sub _tree_construction_main ($) {
7796          }          }
7797    
7798          !!!next-token;          !!!next-token;
7799          redo B;          next B;
7800        } elsif ({        } elsif ({
7801                    ## NOTE: As normal, except acts as a closer for any ...
7802                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7803                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7804          ## has an element in scope          ## has an element in scope
7805          my $i;          my $i;
7806          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7807            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7808            if ({            if ($node->[1] & HEADING_EL) {
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
7809              !!!cp ('t423');              !!!cp ('t423');
7810              $i = $_;              $i = $_;
7811              last INSCOPE;              last INSCOPE;
7812            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7813              !!!cp ('t424');              !!!cp ('t424');
7814              last INSCOPE;              last INSCOPE;
7815            }            }
# Line 6144  sub _tree_construction_main ($) { Line 7817  sub _tree_construction_main ($) {
7817    
7818          unless (defined $i) { # has an element in scope          unless (defined $i) { # has an element in scope
7819            !!!cp ('t425.1');            !!!cp ('t425.1');
7820            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7821                              text => $token->{tag_name}, token => $token);
7822              ## NOTE: Ignore the token.
7823          } else {          } else {
7824            ## Step 1. generate implied end tags            ## Step 1. generate implied end tags
7825            while ({            while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                   dd => 1, dt => 1, li => 1, p => 1,  
                  }->{$self->{open_elements}->[-1]->[1]}) {  
7826              !!!cp ('t422');              !!!cp ('t422');
7827              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
7828            }            }
7829                        
7830            ## Step 2.            ## Step 2.
7831            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7832                      ne $token->{tag_name}) {
7833              !!!cp ('t425');              !!!cp ('t425');
7834              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'unmatched end tag',
7835                                text => $token->{tag_name}, token => $token);
7836            } else {            } else {
7837              !!!cp ('t426');              !!!cp ('t426');
7838            }            }
# Line 6167  sub _tree_construction_main ($) { Line 7842  sub _tree_construction_main ($) {
7842          }          }
7843                    
7844          !!!next-token;          !!!next-token;
7845          redo B;          next B;
7846        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
7847            ## NOTE: As normal, except </p> implies <p> and ...
7848    
7849          ## has an element in scope          ## has an element in scope
7850            my $non_optional;
7851          my $i;          my $i;
7852          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7853            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7854            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & P_EL) {
7855              !!!cp ('t410.1');              !!!cp ('t410.1');
7856              $i = $_;              $i = $_;
7857              last INSCOPE;              last INSCOPE;
7858            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7859              !!!cp ('t411.1');              !!!cp ('t411.1');
7860              last INSCOPE;              last INSCOPE;
7861              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7862                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7863                !!!cp ('t411.2');
7864                #
7865              } else {
7866                !!!cp ('t411.3');
7867                $non_optional ||= $node;
7868                #
7869            }            }
7870          } # INSCOPE          } # INSCOPE
7871    
7872          if (defined $i) {          if (defined $i) {
7873            if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            ## 1. Generate implied end tags
7874              #
7875    
7876              ## 2. If current node != "p", parse error
7877              if ($non_optional) {
7878              !!!cp ('t412.1');              !!!cp ('t412.1');
7879              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'not closed',
7880                                text => $non_optional->[0]->manakai_local_name,
7881                                token => $token);
7882            } else {            } else {
7883              !!!cp ('t414.1');              !!!cp ('t414.1');
7884            }            }
7885    
7886              ## 3. Pop
7887            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7888          } else {          } else {
7889            !!!cp ('t413.1');            !!!cp ('t413.1');
7890            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!parse-error (type => 'unmatched end tag',
7891                              text => $token->{tag_name}, token => $token);
7892    
7893            !!!cp ('t415.1');            !!!cp ('t415.1');
7894            ## As if <p>, then reprocess the current token            ## As if <p>, then reprocess the current token
7895            my $el;            my $el;
7896            !!!create-element ($el, 'p');            !!!create-element ($el, $HTML_NS, 'p',, $token);
7897            $insert->($el);            $insert->($el);
7898            ## NOTE: Not inserted into |$self->{open_elements}|.            ## NOTE: Not inserted into |$self->{open_elements}|.
7899          }          }
7900    
7901          !!!next-token;          !!!next-token;
7902          redo B;          next B;
7903        } elsif ({        } elsif ({
7904                  a => 1,                  a => 1,
7905                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7906                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7907                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7908                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7909          !!!cp ('t427');          !!!cp ('t427');
7910          $formatting_end_tag->($token->{tag_name});          $formatting_end_tag->($token);
7911          redo B;          next B;
7912        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7913          !!!cp ('t428');          !!!cp ('t428');
7914          !!!parse-error (type => 'unmatched end tag:br');          !!!parse-error (type => 'unmatched end tag',
7915                            text => 'br', token => $token);
7916    
7917          ## As if <br>          ## As if <br>
7918          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7919                    
7920          my $el;          my $el;
7921          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7922          $insert->($el);          $insert->($el);
7923                    
7924          ## Ignore the token.          ## Ignore the token.
7925          !!!next-token;          !!!next-token;
7926          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  
           
7927        } else {        } else {
7928            if ($token->{tag_name} eq 'sarcasm') {
7929              sleep 0.001; # take a deep breath
7930            }
7931    
7932          ## Step 1          ## Step 1
7933          my $node_i = -1;          my $node_i = -1;
7934          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7935    
7936          ## Step 2          ## Step 2
7937          S2: {          S2: {
7938            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7939              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7940              if ($node_tag_name eq $token->{tag_name}) {
7941              ## Step 1              ## Step 1
7942              ## generate implied end tags              ## generate implied end tags
7943              while ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
                     dd => 1, dt => 1, li => 1, p => 1,  
                    }->{$self->{open_elements}->[-1]->[1]}) {  
7944                !!!cp ('t430');                !!!cp ('t430');
7945                ## ISSUE: Can this case be reached?                ## NOTE: |<ruby><rt></ruby>|.
7946                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7947                  ## which seems wrong.
7948                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
7949                  $node_i++;
7950              }              }
7951                    
7952              ## Step 2              ## Step 2
7953              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7954                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7955                $current_tag_name =~ tr/A-Z/a-z/;
7956                if ($current_tag_name ne $token->{tag_name}) {
7957                !!!cp ('t431');                !!!cp ('t431');
7958                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7959                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7960                                  text => $self->{open_elements}->[-1]->[0]
7961                                      ->manakai_local_name,
7962                                  token => $token);
7963              } else {              } else {
7964                !!!cp ('t432');                !!!cp ('t432');
7965              }              }
7966                            
7967              ## Step 3              ## Step 3
7968              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7969    
7970              !!!next-token;              !!!next-token;
7971              last S2;              last S2;
7972            } else {            } else {
7973              ## Step 3              ## Step 3
7974              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7975                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7976                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7977                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7978                !!!cp ('t433');                !!!cp ('t433');
7979                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!parse-error (type => 'unmatched end tag',
7980                                  text => $token->{tag_name}, token => $token);
7981                ## Ignore the token                ## Ignore the token
7982                !!!next-token;                !!!next-token;
7983                last S2;                last S2;
             }  
7984    
7985                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7986                  ## 9.27, "a" is a child of <dd> (conforming).  In
7987                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7988                  ## "a" is a child of both <body> and <dd>.
7989                }
7990                
7991              !!!cp ('t434');              !!!cp ('t434');
7992            }            }
7993                        
# Line 6307  sub _tree_construction_main ($) { Line 7998  sub _tree_construction_main ($) {
7998            ## Step 5;            ## Step 5;
7999            redo S2;            redo S2;
8000          } # S2          } # S2
8001          redo B;          next B;
8002        }        }
8003      }      }
8004      redo B;      next B;
8005      } continue { # B
8006        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8007          ## NOTE: The code below is executed in cases where it does not have
8008          ## to be, but it it is harmless even in those cases.
8009          ## has an element in scope
8010          INSCOPE: {
8011            for (reverse 0..$#{$self->{open_elements}}) {
8012              my $node = $self->{open_elements}->[$_];
8013              if ($node->[1] & FOREIGN_EL) {
8014                last INSCOPE;
8015              } elsif ($node->[1] & SCOPING_EL) {
8016                last;
8017              }
8018            }
8019            
8020            ## NOTE: No foreign element in scope.
8021            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8022          } # INSCOPE
8023        }
8024    } # B    } # B
8025    
8026    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 6318  sub _tree_construction_main ($) { Line 8028  sub _tree_construction_main ($) {
8028    ## TODO: script stuffs    ## TODO: script stuffs
8029  } # _tree_construct_main  } # _tree_construct_main
8030    
8031  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8032    my $class = shift;    my $class = shift;
8033    my $node = shift;    my $node = shift;
8034    my $s = \$_[0];    #my $s = \$_[0];
8035    my $onerror = $_[1];    my $onerror = $_[1];
8036      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8037    
8038    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8039    
# Line 6341  sub set_inner_html ($$$) { Line 8052  sub set_inner_html ($$$) {
8052      }      }
8053    
8054      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8055      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8056    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8057      ## TODO: If non-html element      ## TODO: If non-html element
8058    
8059      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8060    
8061    ## TODO: Support for $get_wrapper
8062    
8063      ## Step 1 # MUST      ## Step 1 # MUST
8064      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8065      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 6356  sub set_inner_html ($$$) { Line 8069  sub set_inner_html ($$$) {
8069    
8070      ## Step 8 # MUST      ## Step 8 # MUST
8071      my $i = 0;      my $i = 0;
8072      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8073      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8074      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8075        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8076        $input = $get_wrapper->($input);
8077        $p->{set_nc} = sub {
8078        my $self = shift;        my $self = shift;
8079    
8080        pop @{$self->{prev_char}};        my $char = '';
8081        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8082            $char = $self->{next_nc};
8083            delete $self->{next_nc};
8084            $self->{nc} = ord $char;
8085          } else {
8086            $self->{char_buffer} = '';
8087            $self->{char_buffer_pos} = 0;
8088            
8089            my $count = $input->manakai_read_until
8090                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8091                 $self->{char_buffer_pos});
8092            if ($count) {
8093              $self->{line_prev} = $self->{line};
8094              $self->{column_prev} = $self->{column};
8095              $self->{column}++;
8096              $self->{nc}
8097                  = ord substr ($self->{char_buffer},
8098                                $self->{char_buffer_pos}++, 1);
8099              return;
8100            }
8101            
8102            if ($input->read ($char, 1)) {
8103              $self->{nc} = ord $char;
8104            } else {
8105              $self->{nc} = -1;
8106              return;
8107            }
8108          }
8109    
8110        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8111        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8112        $column++;  
8113          if ($self->{nc} == 0x000A) { # LF
8114        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8115          $line++;          $p->{column} = 0;
         $column = 0;  
8116          !!!cp ('i1');          !!!cp ('i1');
8117        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8118          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8119          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8120          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8121          $column = 0;            $self->{next_nc} = $next;
8122            }
8123            $self->{nc} = 0x000A; # LF # MUST
8124            $p->{line}++;
8125            $p->{column} = 0;
8126          !!!cp ('i2');          !!!cp ('i2');
8127        } 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  
8128          !!!cp ('i4');          !!!cp ('i4');
8129          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8130          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8131        }        }
8132      };      };
8133      $p->{prev_char} = [-1, -1, -1];  
8134      $p->{next_char} = -1;      $p->{read_until} = sub {
8135              #my ($scalar, $specials_range, $offset) = @_;
8136          return 0 if defined $p->{next_nc};
8137    
8138          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8139          my $offset = $_[2] || 0;
8140          
8141          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8142            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8143            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8144              substr ($_[0], $offset)
8145                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8146              my $count = $+[0] - $-[0];
8147              if ($count) {
8148                $p->{column} += $count;
8149                $p->{char_buffer_pos} += $count;
8150                $p->{line_prev} = $p->{line};
8151                $p->{column_prev} = $p->{column} - 1;
8152                $p->{nc} = -1;
8153              }
8154              return $count;
8155            } else {
8156              return 0;
8157            }
8158          } else {
8159            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8160            if ($count) {
8161              $p->{column} += $count;
8162              $p->{column_prev} += $count;
8163              $p->{nc} = -1;
8164            }
8165            return $count;
8166          }
8167        }; # $p->{read_until}
8168    
8169      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8170        my (%opt) = @_;        my (%opt) = @_;
8171        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8172          my $column = $opt{column};
8173          if (defined $opt{token} and defined $opt{token}->{line}) {
8174            $line = $opt{token}->{line};
8175            $column = $opt{token}->{column};
8176          }
8177          warn "Parse error ($opt{type}) at line $line column $column\n";
8178      };      };
8179      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8180        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8181      };      };
8182            
8183        my $char_onerror = sub {
8184          my (undef, $type, %opt) = @_;
8185          $ponerror->(layer => 'encode',
8186                      line => $p->{line}, column => $p->{column} + 1,
8187                      %opt, type => $type);
8188        }; # $char_onerror
8189        $input->onerror ($char_onerror);
8190    
8191      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8192      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8193    
# Line 6419  sub set_inner_html ($$$) { Line 8209  sub set_inner_html ($$$) {
8209          unless defined $p->{content_model};          unless defined $p->{content_model};
8210          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8211    
8212      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8213          ## TODO: Foreign element OK?
8214    
8215      ## Step 3      ## Step 3
8216      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
# Line 6429  sub set_inner_html ($$$) { Line 8220  sub set_inner_html ($$$) {
8220      $doc->append_child ($root);      $doc->append_child ($root);
8221    
8222      ## Step 5 # MUST      ## Step 5 # MUST
8223      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8224    
8225      undef $p->{head_element};      undef $p->{head_element};
8226        undef $p->{head_element_inserted};
8227    
8228      ## Step 6 # MUST      ## Step 6 # MUST
8229      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
# Line 6475  sub set_inner_html ($$$) { Line 8267  sub set_inner_html ($$$) {
8267      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8268    
8269      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8270    
8271        delete $p->{parse_error}; # delete loop
8272    } else {    } else {
8273      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";
8274    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24