/[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.67 by wakaba, Sat Feb 16 03:47:33 2008 UTC revision 1.202 by wakaba, Sat Oct 4 14:31:28 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  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
23  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
24  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    link => 1,  
31    meta => 1,  sub A_EL () { 0b1 }
32    hr => 1,  sub ADDRESS_EL () { 0b10 }
33    br => 1,  sub BODY_EL () { 0b100 }
34    img=> 1,  sub BUTTON_EL () { 0b1000 }
35    embed => 1,  sub CAPTION_EL () { 0b10000 }
36    param => 1,  sub DD_EL () { 0b100000 }
37    area => 1,  sub DIV_EL () { 0b1000000 }
38    col => 1,  sub DT_EL () { 0b10000000 }
39    input => 1,  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 $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  my $c1_entity_char = {  ## 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 61  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  *parse_char_string = \&parse_string;  ## 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
651    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652    ## because the core part of our HTML parser expects a string of character,
653    ## not a string of bytes or code units or anything which might contain a BOM.
654    ## Therefore, any parser interface that accepts a string of bytes,
655    ## such as |parse_byte_string| in this module, must ensure that it does
656    ## strip the BOM and never strip any ZWNBSP.
657    
658  sub parse_string ($$$;$) {  sub parse_char_string ($$$;$$) {
659    my $self = ref $_[0] ? shift : shift->new;    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661    my $s = ref $_[0] ? $_[0] : \($_[0]);    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_char_stream ($$$;$$) {
669      my $self = ref $_[0] ? shift : shift->new;
670      my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 168  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_input_character} = sub {    $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_input_character}};      my $char = '';
688      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707      $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
708      $self->{next_input_character} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
709      $column++;        } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_input_character} == 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_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_input_character} = [-1, -1, -1];  
740    $self->{next_input_character} = -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_input_character} = sub {      level => {must => 'm',
814      $self->{next_input_character} = -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 247  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 258  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 279  sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUO Line 876  sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUO
876  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
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 }
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 295  sub TABLE_IMS ()      { 0b1000000 } Line 913  sub TABLE_IMS ()      { 0b1000000 }
913  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
914  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
915  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922    ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
924    ## NOTE: "after after body" insertion mode.
925  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
926    
927    ## NOTE: "after after frameset" insertion mode.
928  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
929    
930  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
931  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
932  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 311  sub IN_TABLE_IM () { TABLE_IMS } Line 940  sub IN_TABLE_IM () { TABLE_IMS }
940  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 319  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 949  sub IN_COLUMN_GROUP_IM () { 0b10 }
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
952      #$self->{s_kwd}; # state keyword - initialized when used
953      #$self->{entity__value}; # initialized when used
954      #$self->{entity__match}; # initialized when used
955    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
957    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
958    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
959    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
960    $self->{char} = [];    delete $self->{self_closing};
961    # $self->{next_input_character}    $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967    # $self->{escape}    # $self->{escape}
# Line 336  sub _initialize_tokenizer ($) { Line 972  sub _initialize_tokenizer ($) {
972  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
974  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##        ->{name}  ##        ->{name}
980  ##        ->{value}  ##        ->{value}
981  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
982  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 353  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
997  ## contains some requirements that are not detected by the  
998  ## parsing algorithm:  my $is_space = {
999  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1000  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1001  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1002  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1003  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1004  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1005    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA  
1027            $self->{state} = ENTITY_DATA_STATE;        if ($self->{nc} == 0x0026) { # &
1028            !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069                not $self->{escape}) {
1070              !!!cp (1);
1071              ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081              !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1087              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1088                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1090                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1091              }              $self->{s_kwd} = '--';
1092                #
1093              } elsif ($self->{s_kwd} eq '---') {
1094                !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097              } else {
1098                !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105            if (length $self->{s_kwd}) {
1106              !!!cp (5.1);
1107              $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
1119              !!!cp (6);
1120            $self->{state} = TAG_OPEN_STATE;            $self->{state} = TAG_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } else {          } else {
1124              !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1132                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1133              delete $self->{escape};              delete $self->{escape};
1134              } else {
1135                !!!cp (9);
1136            }            }
1137            } else {
1138              !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!emit ({type => END_OF_FILE_TOKEN});          !!!cp (11);
1145            $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149          } else {
1150            !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
1154    
1155        # Anything else        # Anything else
1156        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1157                     data => chr $self->{next_input_character}};                     data => chr $self->{nc},
1158        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1159        !!!next-input-character;                    };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        !!!emit ($token);                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          !!!emit ({type => CHARACTER_TOKEN, data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167            !!!cp (13);
1168            $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!emit ($token);          !!!cp (14);
1171            ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1179              !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            ## reconsume            !!!cp (16);
1189            $self->{state} = DATA_STATE;            #
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1190          }          }
1191    
1192            ## reconsume
1193            $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1201              !!!cp (17);
1202            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206              !!!cp (18);
1207            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            $self->{current_token}            !!!cp (19);
1213              $self->{ct}
1214              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1215                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            $self->{current_token} = {type => START_TAG_TOKEN,            !!!cp (20);
1224                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1225                                        tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1233              !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!parse-error (type => 'pio');            !!!cp (22);
1247              !!!parse-error (type => 'pio',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1251            ## $self->{next_input_character} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!parse-error (type => 'bare stago');            !!!cp (23);
1259              !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
# Line 508  sub _get_next_token ($) { Line 1273  sub _get_next_token ($) {
1273          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1277          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
1278    
1279                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281                redo A;          if (defined $self->{last_stag_name}) {
1282              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            }            $self->{s_kwd} = '';
1284            push @next_char, $self->{next_input_character};            ## Reconsume.
1285                    redo A;
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1286          } else {          } else {
1287            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1288            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289              !!!cp (28);
1290            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1291            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (29);
1302                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1303                = {type => END_TAG_TOKEN,
1304                   tag_name => chr ($self->{nc} + 0x0020),
1305                   line => $l, column => $c};
1306          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{nc} and
1310                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1311          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (30);
1312                            tag_name => chr ($self->{next_input_character})};          $self->{ct} = {type => END_TAG_TOKEN,
1313                                      tag_name => chr ($self->{nc}),
1314                                      line => $l, column => $c};
1315          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1316          !!!next-input-character;          !!!next-input-character;
1317          redo A;          redo A;
1318        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1319          !!!parse-error (type => 'empty end tag');          !!!cp (31);
1320            !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1327            !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338            !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343                                      column => $self->{column_prev} - 1,
1344                                     };
1345            ## NOTE: $self->{nc} is intentionally left as is.
1346            ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1406            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1407          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408          !!!next-input-character;          !!!next-input-character;
1409          redo A;          redo A;
1410        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1411          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412            $self->{current_token}->{first_start_tag}            !!!cp (35);
1413                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1415            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1417              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1418            }            #  !!! cp (36);
1419              #  !!! parse-error (type => 'end tag attribute');
1420              #} else {
1421                !!!cp (37);
1422              #}
1423          } else {          } else {
1424            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1425          }          }
1426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1427          !!!next-input-character;          !!!next-input-character;
1428    
1429          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1430    
1431          redo A;          redo A;
1432        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1435            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            $self->{current_token}->{first_start_tag}            !!!cp (39);
1444                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1446            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1448              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1449            }            #  !!! cp (40);
1450              #  !!! parse-error (type => 'end tag attribute');
1451              #} else {
1452                !!!cp (41);
1453              #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1467          redo A;          redo A;
1468        } else {        } else {
1469          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1470            $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1478            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            $self->{current_token}->{first_start_tag}            !!!cp (46);
1485                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1487            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1489                !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491              } else {
1492                !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1506                                value => ''};          $self->{ca}
1507                = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            $self->{current_token}->{first_start_tag}            !!!cp (52);
1522                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1524            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1526                !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1538    
1539          redo A;          redo A;
1540        } else {        } else {
1541          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1542                                value => ''};               0x0022 => 1, # "
1543                 0x0027 => 1, # '
1544                 0x003D => 1, # =
1545                }->{$self->{nc}}) {
1546              !!!cp (55);
1547              !!!parse-error (type => 'bad attribute name');
1548            } else {
1549              !!!cp (56);
1550            }
1551            $self->{ca}
1552                = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1564            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565          } else {            ## Discard $self->{ca} # MUST
1566            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1567              = $self->{current_attribute};            !!!cp (58);
1568              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569                = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1574            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580            !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            $self->{current_token}->{first_start_tag}            !!!cp (61);
1589                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {            !!!cp (62);
1592            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1608            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1617          redo A;          redo A;
1618        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            $self->{current_token}->{first_start_tag}            !!!cp (66);
1623                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1625            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1627                !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629              } else {
1630                ## NOTE: This state should never be reached.
1631                !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1644                $self->{nc} == 0x0027) { # '
1645              !!!cp (69);
1646              !!!parse-error (type => 'bad attribute name');
1647            } else {
1648              !!!cp (70);
1649            }
1650            $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1657            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662            !!!cp (72);
1663          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            $self->{current_token}->{first_start_tag}            !!!cp (73);
1669                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1671            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1673                !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675              } else {
1676                ## NOTE: This state should never be reached.
1677                !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1691                                value => ''};          $self->{ca}
1692                = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            $self->{current_token}->{first_start_tag}            !!!cp (79);
1707                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1709            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1711                !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713              } else {
1714                ## NOTE: This state should never be reached.
1715                !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1728                                value => ''};              $self->{nc} == 0x0027) { # '
1729              !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1744            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749            !!!cp (84);
1750          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754            !!!cp (85);
1755          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759            !!!cp (86);
1760          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1765            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1767            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1771                !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773              } else {
1774                ## NOTE: This state should never be reached.
1775                !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            $self->{current_token}->{first_start_tag}            !!!cp (90);
1790                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1792            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1794                !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796              } else {
1797                ## NOTE: This state should never be reached.
1798                !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1811              !!!cp (93);
1812              !!!parse-error (type => 'bad attribute value');
1813            } else {
1814              !!!cp (94);
1815            }
1816            $self->{ca}->{value} .= chr ($self->{nc});
1817          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          !!!cp (95);
1824            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825            !!!next-input-character;
1826            redo A;
1827          } elsif ($self->{nc} == 0x0026) { # &
1828            !!!cp (96);
1829            ## NOTE: In the spec, the tokenizer is switched to the
1830            ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = $self->{state};  
         $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            $self->{current_token}->{first_start_tag}            !!!cp (97);
1842                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1844            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1846                !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848              } else {
1849                ## NOTE: This state should never be reached.
1850                !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1863            $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          !!!cp (101);
1875            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876            !!!next-input-character;
1877            redo A;
1878          } elsif ($self->{nc} == 0x0026) { # &
1879            !!!cp (102);
1880            ## NOTE: In the spec, the tokenizer is switched to the
1881            ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = $self->{state};  
         $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            $self->{current_token}->{first_start_tag}            !!!cp (103);
1893                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1895            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1897                !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1914            $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1925            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
           $self->{next_input_character} == 0x000B or # HT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1926          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927          !!!next-input-character;          !!!next-input-character;
1928          redo A;          redo A;
1929        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1930          $self->{last_attribute_value_state} = $self->{state};          !!!cp (108);
1931          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1932          !!!next-input-character;          ## "entity in attribute value state".  In this implementation, the
1933          redo A;          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934        } elsif ($self->{next_input_character} == 0x003E) { # >          ## implementation of the "consume a character reference" algorithm.
1935          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          $self->{entity_add} = -1;
1936            $self->{current_token}->{first_start_tag}          $self->{prev_state} = $self->{state};
1937                = not defined $self->{last_emitted_start_tag_name};          $self->{state} = ENTITY_STATE;
1938            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          !!!next-input-character;
1939          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          redo A;
1940          } elsif ($self->{nc} == 0x003E) { # >
1941            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942              !!!cp (109);
1943              $self->{last_stag_name} = $self->{ct}->{tag_name};
1944            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1947                !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949              } else {
1950                ## NOTE: This state should never be reached.
1951                !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            $self->{current_token}->{first_start_tag}            !!!cp (112);
1966                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1968            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1970                !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972              } else {
1973                ## NOTE: This state should never be reached.
1974                !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1983    
1984          redo A;          redo A;
1985        } else {        } else {
1986          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1987                 0x0022 => 1, # "
1988                 0x0027 => 1, # '
1989                 0x003D => 1, # =
1990                }->{$self->{nc}}) {
1991              !!!cp (115);
1992              !!!parse-error (type => 'bad attribute value');
1993            } else {
1994              !!!cp (116);
1995            }
1996            $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
2005      } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2007            !!!cp (118);
2008            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009            !!!next-input-character;
2010            redo A;
2011          } elsif ($self->{nc} == 0x003E) { # >
2012            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013              !!!cp (119);
2014              $self->{last_stag_name} = $self->{ct}->{tag_name};
2015            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2016              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017              if ($self->{ct}->{attributes}) {
2018                !!!cp (120);
2019                !!!parse-error (type => 'end tag attribute');
2020              } else {
2021                ## NOTE: This state should never be reached.
2022                !!!cp (121);
2023              }
2024            } else {
2025              die "$0: $self->{ct}->{type}: Unknown token type";
2026            }
2027            $self->{state} = DATA_STATE;
2028            !!!next-input-character;
2029    
2030            !!!emit ($self->{ct}); # start tag or end tag
2031    
2032        unless (defined $token) {          redo A;
2033          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036            !!!next-input-character;
2037            redo A;
2038          } elsif ($self->{nc} == -1) {
2039            !!!parse-error (type => 'unclosed tag');
2040            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041              !!!cp (122.3);
2042              $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051            } else {
2052              die "$0: $self->{ct}->{type}: Unknown token type";
2053            }
2054            $self->{state} = DATA_STATE;
2055            ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057            redo A;
2058        } else {        } else {
2059          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2060          $self->{current_attribute}->{has_reference} = $token->{has_reference};          !!!parse-error (type => 'no space between attributes');
2061          ## ISSUE: spec says "append the returned character token to the current attribute's value"          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062            ## reconsume
2063            redo A;
2064        }        }
2065        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066          if ($self->{nc} == 0x003E) { # >
2067            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068              !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072              if ($self->{ct}->{attributes}) {
2073                !!!cp ('124.4');
2074                !!!parse-error (type => 'end tag attribute');
2075              } else {
2076                !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2085        # next-input-character is already done          !!!next-input-character;
2086        redo A;  
2087            !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103            } else {
2104              die "$0: $self->{ct}->{type}: Unknown token type";
2105            }
2106            $self->{state} = DATA_STATE;
2107            ## Reconsume.
2108            !!!emit ($self->{ct}); # start tag or end tag
2109            redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       my $token = {type => COMMENT_TOKEN, data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
2120    
2121            !!!emit ($token);        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122          ## consumes characters one-by-one basis.
2123          
2124          if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127            !!!next-input-character;
2128    
2129            redo A;          !!!emit ($self->{ct}); # comment
2130          } elsif ($self->{next_input_character} == -1) {          redo A;
2131            $self->{state} = DATA_STATE;        } elsif ($self->{nc} == -1) {
2132            ## reconsume          !!!cp (125);
2133            $self->{state} = DATA_STATE;
2134            ## reconsume
2135    
2136            !!!emit ($token);          !!!emit ($self->{ct}); # comment
2137            redo A;
2138          } else {
2139            !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            redo A;          ## Stay in the state.
2146          } else {          !!!next-input-character;
2147            $token->{data} .= chr ($self->{next_input_character});          redo A;
2148            !!!next-input-character;        }
           redo BC;  
         }  
       } # BC  
2149      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
   
       my @next_char;  
       push @next_char, $self->{next_input_character};  
2151                
2152        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2153            !!!cp (133);
2154            $self->{state} = MD_HYPHEN_STATE;
2155          !!!next-input-character;          !!!next-input-character;
2156          push @next_char, $self->{next_input_character};          redo A;
2157          if ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2158            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};                 $self->{nc} == 0x0064) { # d
2159            $self->{state} = COMMENT_START_STATE;          ## ASCII case-insensitive.
2160            !!!next-input-character;          !!!cp (130);
2161            redo A;          $self->{state} = MD_DOCTYPE_STATE;
2162          }          $self->{s_kwd} = chr $self->{nc};
       } elsif ($self->{next_input_character} == 0x0044 or # D  
                $self->{next_input_character} == 0x0064) { # d  
2163          !!!next-input-character;          !!!next-input-character;
2164          push @next_char, $self->{next_input_character};          redo A;
2165          if ($self->{next_input_character} == 0x004F or # O        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166              $self->{next_input_character} == 0x006F) { # o                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167            !!!next-input-character;                 $self->{nc} == 0x005B) { # [
2168            push @next_char, $self->{next_input_character};          !!!cp (135.4);                
2169            if ($self->{next_input_character} == 0x0043 or # C          $self->{state} = MD_CDATA_STATE;
2170                $self->{next_input_character} == 0x0063) { # c          $self->{s_kwd} = '[';
2171              !!!next-input-character;          !!!next-input-character;
2172              push @next_char, $self->{next_input_character};          redo A;
2173              if ($self->{next_input_character} == 0x0054 or # T        } else {
2174                  $self->{next_input_character} == 0x0074) { # t          !!!cp (136);
               !!!next-input-character;  
               push @next_char, $self->{next_input_character};  
               if ($self->{next_input_character} == 0x0059 or # Y  
                   $self->{next_input_character} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_input_character};  
                 if ($self->{next_input_character} == 0x0050 or # P  
                     $self->{next_input_character} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_input_character};  
                   if ($self->{next_input_character} == 0x0045 or # E  
                       $self->{next_input_character} == 0x0065) { # e  
                     ## ISSUE: What a stupid code this is!  
                     $self->{state} = DOCTYPE_STATE;  
                     !!!next-input-character;  
                     redo A;  
                   }  
                 }  
               }  
             }  
           }  
         }  
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180          ## Reconsume.
2181        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                      line => $self->{line_prev},
2192                                      column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2298        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2299            !!!cp (137);
2300          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2304            !!!cp (138);
2305          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2306          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2307          !!!next-input-character;          !!!next-input-character;
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2310    
2311          redo A;          redo A;
2312        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2313            !!!cp (139);
2314          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2315          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2316          ## reconsume          ## reconsume
2317    
2318          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2319    
2320          redo A;          redo A;
2321        } else {        } else {
2322          $self->{current_token}->{data} # comment          !!!cp (140);
2323              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2324                .= chr ($self->{nc});
2325          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2326          !!!next-input-character;          !!!next-input-character;
2327          redo A;          redo A;
2328        }        }
2329      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331            !!!cp (141);
2332          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2333          !!!next-input-character;          !!!next-input-character;
2334          redo A;          redo A;
2335        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336            !!!cp (142);
2337          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2338          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2339          !!!next-input-character;          !!!next-input-character;
2340    
2341          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2342    
2343          redo A;          redo A;
2344        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2345            !!!cp (143);
2346          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2347          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2348          ## reconsume          ## reconsume
2349    
2350          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2351    
2352          redo A;          redo A;
2353        } else {        } else {
2354          $self->{current_token}->{data} # comment          !!!cp (144);
2355              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2356                .= '-' . chr ($self->{nc});
2357          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        }        }
2361      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2362        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363            !!!cp (145);
2364          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2368            !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2378            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389            !!!cp (148);
2390          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2394            !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2404            $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411            !!!cp (151);
2412          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2420          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2421                            line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2428            !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2438          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2439                            line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2448            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2449          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2450          !!!next-input-character;          !!!next-input-character;
2451          redo A;          redo A;
2452        } else {        } else {
2453            !!!cp (156);
2454          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2455          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2456          ## reconsume          ## reconsume
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2461            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466            !!!cp (158);
2467          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2475            !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2478          ## reconsume          ## reconsume
2479    
2480          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          $self->{current_token}          !!!cp (160);
2485              = {type => DOCTYPE_TOKEN,          $self->{ct}->{name} = chr $self->{nc};
2486                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
                correct => 1};  
 ## ISSUE: "Set the token's name name to the" in the spec  
2487          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2494            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2495          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499            !!!cp (162);
2500          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507            !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token}->{name}          !!!cp (164);
2518            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2519              .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2526            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531            !!!cp (166);
2532          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2554              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2560        } else {        } else {
2561            !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568          }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604            $self->{state} = BOGUS_DOCTYPE_STATE;
2605            ## Reconsume.
2606            redo A;
2607        }        }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2644        $self->{state} = BOGUS_DOCTYPE_STATE;          ## Reconsume.
2645        # next-input-character is already done          redo A;
2646        redo A;        }
2647      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648        if ({        if ($is_space->{$self->{nc}}) {
2649              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (181);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2655            $self->{ct}->{pubid} = ''; # DOCTYPE
2656          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2661            $self->{ct}->{pubid} = ''; # DOCTYPE
2662          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688            !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690            $self->{ct}->{quirks} = 1;
2691    
2692          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698            !!!cp (187);
2699          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2703            !!!cp (188);
2704            !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706            $self->{state} = DATA_STATE;
2707            !!!next-input-character;
2708    
2709            $self->{ct}->{quirks} = 1;
2710            !!!emit ($self->{ct}); # DOCTYPE
2711    
2712            redo A;
2713          } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2726              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2727                .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737            !!!cp (191);
2738          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743            !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745            $self->{state} = DATA_STATE;
2746            !!!next-input-character;
2747    
2748            $self->{ct}->{quirks} = 1;
2749            !!!emit ($self->{ct}); # DOCTYPE
2750    
2751            redo A;
2752          } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2765              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2766                .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
2776              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2782            $self->{ct}->{sysid} = ''; # DOCTYPE
2783          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2788            $self->{ct}->{sysid} = ''; # DOCTYPE
2789          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793            !!!cp (198);
2794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2801            !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812            !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814            $self->{ct}->{quirks} = 1;
2815    
2816          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
2822              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2828            $self->{ct}->{sysid} = ''; # DOCTYPE
2829          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2834            $self->{ct}->{sysid} = ''; # DOCTYPE
2835          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2849            !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860            !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862            $self->{ct}->{quirks} = 1;
2863    
2864          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870            !!!cp (207);
2871          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2875            !!!cp (208);
2876            !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878            $self->{state} = DATA_STATE;
2879            !!!next-input-character;
2880    
2881            $self->{ct}->{quirks} = 1;
2882            !!!emit ($self->{ct}); # DOCTYPE
2883    
2884            redo A;
2885          } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2898              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2899                .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909            !!!cp (211);
2910          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2914            !!!cp (212);
2915            !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917            $self->{state} = DATA_STATE;
2918            !!!next-input-character;
2919    
2920            $self->{ct}->{quirks} = 1;
2921            !!!emit ($self->{ct}); # DOCTYPE
2922    
2923            redo A;
2924          } elsif ($self->{nc} == -1) {
2925            !!!cp (213);
2926          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2937              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2938                .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
2948              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953            !!!cp (216);
2954          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2963          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2964          ## reconsume          ## reconsume
2965    
2966          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971            !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973            #$self->{ct}->{quirks} = 1;
2974    
2975          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981            !!!cp (219);
2982          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998            !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3035    my ($self, $in_attr) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3043      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      ## No error          !!!next-input-character;
3045      return undef;          redo A;
3046    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3047      !!!next-input-character;          !!!cp (221.6);
3048      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3049          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3050        my $code;          ## Reconsume.
3051        X: {          redo A;
3052          my $x_char = $self->{next_input_character};        }
3053          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3055              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3056            $code ||= 0;          !!!next-input-character;
3057            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3058            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3059            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3060          } else {          } else {
3061            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063          }          }
3064            redo A;
3065          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3067            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
           !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!parse-error (type => 'CR character reference');  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1};  
       } # X  
     } elsif (0x0030 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x0039) { # 0..9  
       my $code = $self->{next_input_character} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_input_character} and  
                 $self->{next_input_character} <= 0x0039) { # 0..9  
         $code *= 10;  
         $code += $self->{next_input_character} - 0x0030;  
           
3069          !!!next-input-character;          !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181            !!!cp (1012);
3182            $self->{s_kwd} *= 10;
3183            $self->{s_kwd} += $self->{nc} - 0x0030;
3184            
3185            ## Stay in the state.
3186            !!!next-input-character;
3187            redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
3189            !!!cp (1013);
3190          !!!next-input-character;          !!!next-input-character;
3191            #
3192        } else {        } else {
3193            !!!cp (1014);
3194          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203            !!!cp (1015);
3204            !!!parse-error (type => 'invalid character reference',
3205                            text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208          } elsif ($code > 0x10FFFF) {
3209            !!!cp (1016);
3210            !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
3214          }
3215    
3216          if ($self->{prev_state} == DATA_STATE) {
3217            !!!cp (992);
3218            $self->{state} = $self->{prev_state};
3219            ## Reconsume.
3220            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221                      line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3317          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3322        }        }
3323          
3324        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3325      } else {          !!!cp (988);
3326        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3327        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3328        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329        return undef;                    line => $l, column => $c,
3330      }                   });
3331    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3332              $self->{next_input_character} <= 0x005A) or        } else {
3333             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3334              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3335      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3336      !!!next-input-character;          $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338      my $value = $entity_name;          redo A;
3339      my $match = 0;        }
3340      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3344             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3345             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3346               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3347              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3348               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3349              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3350               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3351              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3352        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3353        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3354          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3355            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356            $match = 1;              $self->{entity__match} = 1;
3357            !!!next-input-character;              !!!next-input-character;
3358            last;              #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367          } else {          } else {
3368            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3369            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3375        }        }
3376      }  
3377              my $data;
3378      if ($match > 0) {        my $has_ref;
3379        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($self->{entity__match} > 0) {
3380      } elsif ($match < 0) {          !!!cp (1023);
3381        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3382        if ($in_attr and $match < -1) {          $has_ref = 1;
3383          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396            }
3397        } else {        } else {
3398          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!cp (1026);
3399            !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: "No characters are consumed" in the spec.  
       return {type => CHARACTER_TOKEN, data => '&'.$value};  
3436      }      }
3437    } else {    } # A  
3438      ## no characters are consumed  
3439      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3440      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 1956  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1982  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474        
3475    !!!next-token;    !!!next-token;
3476    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479      undef $self->{head_element_inserted};
3480    $self->{open_elements} = [];    $self->{open_elements} = [];
3481    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3482    
3483      ## NOTE: The "initial" insertion mode.
3484    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3485    
3486      ## NOTE: The "before html" insertion mode.
3487    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3488      $self->{insertion_mode} = BEFORE_HEAD_IM;
3489    
3490      ## NOTE: The "before head" insertion mode and so on.
3491    $self->_tree_construction_main;    $self->_tree_construction_main;
3492  } # _construct_tree  } # _construct_tree
3493    
3494  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3495    my $self = shift;    my $self = shift;
3496    
3497      ## NOTE: "initial" insertion mode
3498    
3499    INITIAL: {    INITIAL: {
3500      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3501        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 2002  sub _tree_construction_initial ($) { Line 3503  sub _tree_construction_initial ($) {
3503        ## language.        ## language.
3504        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3505        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3506        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3507        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3508            defined $token->{public_identifier} or            defined $token->{sysid}) {
3509            defined $token->{system_identifier}) {          !!!cp ('t1');
3510          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3511        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3512          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3513          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3514          } elsif (defined $token->{pubid}) {
3515            if ($token->{pubid} eq 'XSLT-compat') {
3516              !!!cp ('t1.2');
3517              !!!parse-error (type => 'XSLT-compat', token => $token,
3518                              level => $self->{level}->{should});
3519            } else {
3520              !!!parse-error (type => 'not HTML5', token => $token);
3521            }
3522          } else {
3523            !!!cp ('t3');
3524            #
3525        }        }
3526                
3527        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3528          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3529        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3530            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3531        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3532            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3533        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3534        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3535        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3536                
3537        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3538            !!!cp ('t4');
3539          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3540        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3541          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3542          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3543          if ({          my $prefix = [
3544            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3545            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3548            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3550            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3551            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3552            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3553            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3554            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3555            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3556            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3557            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3558            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3559            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3560            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3561            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3562            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3563            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3564            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3565            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3566            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3568            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3570            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3572            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3574            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3575            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3577            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3578            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3579            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3580            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3581            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3587            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3588            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3589            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3592            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3593            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3594            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3595            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3596            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3597            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3598            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3599            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3600            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3601            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3602            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3603            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3604            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3605            "-//W3C//DTD W3 HTML//EN" => 1,            }
3606            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3607            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3608            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3609            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3610            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3611            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3612            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3613          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3614                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3615            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3616                !!!cp ('t6');
3617              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3618            } else {            } else {
3619                !!!cp ('t7');
3620              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3621            }            }
3622          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3623                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3624              !!!cp ('t8');
3625            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3626            } else {
3627              !!!cp ('t9');
3628          }          }
3629          } else {
3630            !!!cp ('t10');
3631        }        }
3632        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3633          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3634          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3635          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") {
3636              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3637              ## marked as quirks.
3638            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3639              !!!cp ('t11');
3640            } else {
3641              !!!cp ('t12');
3642          }          }
3643          } else {
3644            !!!cp ('t13');
3645        }        }
3646                
3647        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3648        !!!next-token;        !!!next-token;
3649        return;        return;
3650      } elsif ({      } elsif ({
# Line 2127  sub _tree_construction_initial ($) { Line 3652  sub _tree_construction_initial ($) {
3652                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
3653                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3654               }->{$token->{type}}) {               }->{$token->{type}}) {
3655        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3656          !!!parse-error (type => 'no DOCTYPE', token => $token);
3657        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3658        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3659        ## reprocess        ## reprocess
3660          !!!ack-later;
3661        return;        return;
3662      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3663        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3664          ## Ignore the token          ## Ignore the token
3665    
3666          unless (length $token->{data}) {          unless (length $token->{data}) {
3667            ## Stay in the phase            !!!cp ('t15');
3668              ## Stay in the insertion mode.
3669            !!!next-token;            !!!next-token;
3670            redo INITIAL;            redo INITIAL;
3671            } else {
3672              !!!cp ('t16');
3673          }          }
3674          } else {
3675            !!!cp ('t17');
3676        }        }
3677    
3678        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3679        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3680        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3681        ## reprocess        ## reprocess
3682        return;        return;
3683      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3684          !!!cp ('t18');
3685        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3686        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3687                
3688        ## Stay in the phase.        ## Stay in the insertion mode.
3689        !!!next-token;        !!!next-token;
3690        redo INITIAL;        redo INITIAL;
3691      } else {      } else {
3692        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
3693      }      }
3694    } # INITIAL    } # INITIAL
3695    
3696      die "$0: _tree_construction_initial: This should be never reached";
3697  } # _tree_construction_initial  } # _tree_construction_initial
3698    
3699  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3700    my $self = shift;    my $self = shift;
3701    
3702      ## NOTE: "before html" insertion mode.
3703        
3704    B: {    B: {
3705        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3706          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3707            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3708          ## Ignore the token          ## Ignore the token
3709          ## Stay in the phase          ## Stay in the insertion mode.
3710          !!!next-token;          !!!next-token;
3711          redo B;          redo B;
3712        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
3713            !!!cp ('t20');
3714          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3715          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3716          ## Stay in the phase          ## Stay in the insertion mode.
3717          !!!next-token;          !!!next-token;
3718          redo B;          redo B;
3719        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3720          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3721            ## Ignore the token.            ## Ignore the token.
3722    
3723            unless (length $token->{data}) {            unless (length $token->{data}) {
3724              ## Stay in the phase              !!!cp ('t21');
3725                ## Stay in the insertion mode.
3726              !!!next-token;              !!!next-token;
3727              redo B;              redo B;
3728              } else {
3729                !!!cp ('t22');
3730            }            }
3731            } else {
3732              !!!cp ('t23');
3733          }          }
3734    
3735          $self->{application_cache_selection}->(undef);          $self->{application_cache_selection}->(undef);
3736    
3737          #          #
3738        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3739          if ($token->{tag_name} eq 'html' and          if ($token->{tag_name} eq 'html') {
3740              $token->{attributes}->{manifest}) {            my $root_element;
3741            $self->{application_cache_selection}            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3742                 ->($token->{attributes}->{manifest}->{value});            $self->{document}->append_child ($root_element);
3743            ## ISSUE: No relative reference resolution?            push @{$self->{open_elements}},
3744                  [$root_element, $el_category->{html}];
3745    
3746              if ($token->{attributes}->{manifest}) {
3747                !!!cp ('t24');
3748                $self->{application_cache_selection}
3749                    ->($token->{attributes}->{manifest}->{value});
3750                ## ISSUE: Spec is unclear on relative references.
3751                ## According to Hixie (#whatwg 2008-03-19), it should be
3752                ## resolved against the base URI of the document in HTML
3753                ## or xml:base of the element in XHTML.
3754              } else {
3755                !!!cp ('t25');
3756                $self->{application_cache_selection}->(undef);
3757              }
3758    
3759              !!!nack ('t25c');
3760    
3761              !!!next-token;
3762              return; ## Go to the "before head" insertion mode.
3763          } else {          } else {
3764            $self->{application_cache_selection}->(undef);            !!!cp ('t25.1');
3765              #
3766          }          }
   
         ## ISSUE: There is an issue in the spec  
         #  
3767        } elsif ({        } elsif ({
3768                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
3769                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
3770                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3771          $self->{application_cache_selection}->(undef);          !!!cp ('t26');
   
         ## ISSUE: There is an issue in the spec  
3772          #          #
3773        } else {        } else {
3774          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3775        }        }
3776    
3777        my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3778        $self->{document}->append_child ($root_element);      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3779        push @{$self->{open_elements}}, [$root_element, 'html'];      $self->{document}->append_child ($root_element);
3780        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3781        #redo B;  
3782        return; ## Go to the main phase.      $self->{application_cache_selection}->(undef);
3783    
3784        ## NOTE: Reprocess the token.
3785        !!!ack-later;
3786        return; ## Go to the "before head" insertion mode.
3787    } # B    } # B
3788    
3789      die "$0: _tree_construction_root_element: This should never be reached";
3790  } # _tree_construction_root_element  } # _tree_construction_root_element
3791    
3792  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2236  sub _reset_insertion_mode ($) { Line 3801  sub _reset_insertion_mode ($) {
3801            
3802      ## Step 3      ## Step 3
3803      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3804        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3805          $last = 1;          $last = 1;
3806          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3807            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3808                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3809              #          } else {
3810            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3811          }          }
3812        }        }
3813              
3814        ## Step 4..13        ## Step 4..14
3815        my $new_mode = {        my $new_mode;
3816          if ($node->[1] & FOREIGN_EL) {
3817            !!!cp ('t28.1');
3818            ## NOTE: Strictly spaking, the line below only applies to MathML and
3819            ## SVG elements.  Currently the HTML syntax supports only MathML and
3820            ## SVG elements as foreigners.
3821            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3822          } elsif ($node->[1] & TABLE_CELL_EL) {
3823            if ($last) {
3824              !!!cp ('t28.2');
3825              #
3826            } else {
3827              !!!cp ('t28.3');
3828              $new_mode = IN_CELL_IM;
3829            }
3830          } else {
3831            !!!cp ('t28.4');
3832            $new_mode = {
3833                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3834                        td => IN_CELL_IM,                        ## NOTE: |option| and |optgroup| do not set
3835                        th => IN_CELL_IM,                        ## insertion mode to "in select" by themselves.
3836                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3837                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3838                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2268  sub _reset_insertion_mode ($) { Line 3843  sub _reset_insertion_mode ($) {
3843                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3844                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3845                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3846                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3847          }
3848        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3849                
3850        ## Step 14        ## Step 15
3851        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3852          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3853              !!!cp ('t29');
3854            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3855          } else {          } else {
3856              ## ISSUE: Can this state be reached?
3857              !!!cp ('t30');
3858            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3859          }          }
3860          return;          return;
3861          } else {
3862            !!!cp ('t31');
3863        }        }
3864                
3865        ## Step 15        ## Step 16
3866        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3867                
3868        ## Step 16        ## Step 17
3869        $i--;        $i--;
3870        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3871                
3872        ## Step 17        ## Step 18
3873        redo S3;        redo S3;
3874      } # S3      } # S3
3875    
3876      die "$0: _reset_insertion_mode: This line should never be reached";
3877  } # _reset_insertion_mode  } # _reset_insertion_mode
3878    
3879  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2312  sub _tree_construction_main ($) { Line 3895  sub _tree_construction_main ($) {
3895      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3896      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3897        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3898            !!!cp ('t32');
3899          return;          return;
3900        }        }
3901      }      }
# Line 2326  sub _tree_construction_main ($) { Line 3910  sub _tree_construction_main ($) {
3910    
3911        ## Step 6        ## Step 6
3912        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3913            !!!cp ('t33_1');
3914          #          #
3915        } else {        } else {
3916          my $in_open_elements;          my $in_open_elements;
3917          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3918            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3919                !!!cp ('t33');
3920              $in_open_elements = 1;              $in_open_elements = 1;
3921              last OE;              last OE;
3922            }            }
3923          }          }
3924          if ($in_open_elements) {          if ($in_open_elements) {
3925              !!!cp ('t34');
3926            #            #
3927          } else {          } else {
3928              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3929              !!!cp ('t35');
3930            redo S4;            redo S4;
3931          }          }
3932        }        }
# Line 2360  sub _tree_construction_main ($) { Line 3949  sub _tree_construction_main ($) {
3949    
3950        ## Step 11        ## Step 11
3951        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3952            !!!cp ('t36');
3953          ## Step 7'          ## Step 7'
3954          $i++;          $i++;
3955          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3956                    
3957          redo S7;          redo S7;
3958        }        }
3959    
3960          !!!cp ('t37');
3961      } # S7      } # S7
3962    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3963    
3964    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3965      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3966        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3967            !!!cp ('t38');
3968          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3969          return;          return;
3970        }        }
3971      }      }
3972    
3973        !!!cp ('t39');
3974    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3975    
3976    my $parse_rcdata = sub ($$) {    my $insert;
3977      my ($content_model_flag, $insert) = @_;  
3978      my $parse_rcdata = sub ($) {
3979        my ($content_model_flag) = @_;
3980    
3981      ## Step 1      ## Step 1
3982      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3983      my $el;      my $el;
3984      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3985    
3986      ## Step 2      ## Step 2
3987      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3988    
3989      ## Step 3      ## Step 3
3990      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2395  sub _tree_construction_main ($) { Line 3992  sub _tree_construction_main ($) {
3992    
3993      ## Step 4      ## Step 4
3994      my $text = '';      my $text = '';
3995        !!!nack ('t40.1');
3996      !!!next-token;      !!!next-token;
3997      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3998          !!!cp ('t40');
3999        $text .= $token->{data};        $text .= $token->{data};
4000        !!!next-token;        !!!next-token;
4001      }      }
4002    
4003      ## Step 5      ## Step 5
4004      if (length $text) {      if (length $text) {
4005          !!!cp ('t41');
4006        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
4007        $el->append_child ($text);        $el->append_child ($text);
4008      }      }
# Line 2411  sub _tree_construction_main ($) { Line 4011  sub _tree_construction_main ($) {
4011      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
4012    
4013      ## Step 7      ## Step 7
4014      if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
4015            $token->{tag_name} eq $start_tag_name) {
4016          !!!cp ('t42');
4017        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4018      } else {      } else {
4019        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
4020          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4021            !!!cp ('t43');
4022            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4023          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4024            !!!cp ('t44');
4025            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4026          } else {
4027            die "$0: $content_model_flag in parse_rcdata";
4028          }
4029      }      }
4030      !!!next-token;      !!!next-token;
4031    }; # $parse_rcdata    }; # $parse_rcdata
4032    
4033    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4034      my $script_el;      my $script_el;
4035      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4036      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4037    
4038      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4039      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4040            
4041      my $text = '';      my $text = '';
4042        !!!nack ('t45.1');
4043      !!!next-token;      !!!next-token;
4044      while ($token->{type} == CHARACTER_TOKEN) {      while ($token->{type} == CHARACTER_TOKEN) {
4045          !!!cp ('t45');
4046        $text .= $token->{data};        $text .= $token->{data};
4047        !!!next-token;        !!!next-token;
4048      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4049      if (length $text) {      if (length $text) {
4050          !!!cp ('t46');
4051        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4052      }      }
4053                                
# Line 2446  sub _tree_construction_main ($) { Line 4055  sub _tree_construction_main ($) {
4055    
4056      if ($token->{type} == END_TAG_TOKEN and      if ($token->{type} == END_TAG_TOKEN and
4057          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4058          !!!cp ('t47');
4059        ## Ignore the token        ## Ignore the token
4060      } else {      } else {
4061        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4062          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4063        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4064        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4065      }      }
4066            
4067      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4068          !!!cp ('t49');
4069        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4070      } else {      } else {
4071          !!!cp ('t50');
4072        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4073        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4074    
# Line 2469  sub _tree_construction_main ($) { Line 4082  sub _tree_construction_main ($) {
4082      !!!next-token;      !!!next-token;
4083    }; # $script_start_tag    }; # $script_start_tag
4084    
4085      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4086      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4087      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4088      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4089    
4090    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4091      my $tag_name = shift;      my $end_tag_token = shift;
4092        my $tag_name = $end_tag_token->{tag_name};
4093    
4094        ## NOTE: The adoption agency algorithm (AAA).
4095    
4096      FET: {      FET: {
4097        ## Step 1        ## Step 1
4098        my $formatting_element;        my $formatting_element;
4099        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4100        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4101          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4102              !!!cp ('t52');
4103              last AFE;
4104            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4105                         eq $tag_name) {
4106              !!!cp ('t51');
4107            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4108            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4109            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4110          }          }
4111        } # AFE        } # AFE
4112        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4113          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4114            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4115          ## Ignore the token          ## Ignore the token
4116          !!!next-token;          !!!next-token;
4117          return;          return;
# Line 2498  sub _tree_construction_main ($) { Line 4123  sub _tree_construction_main ($) {
4123          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4124          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4125            if ($in_scope) {            if ($in_scope) {
4126                !!!cp ('t54');
4127              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4128              last INSCOPE;              last INSCOPE;
4129            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4130              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4131                !!!parse-error (type => 'unmatched end tag',
4132                                text => $token->{tag_name},
4133                                token => $end_tag_token);
4134              ## Ignore the token              ## Ignore the token
4135              !!!next-token;              !!!next-token;
4136              return;              return;
4137            }            }
4138          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4139                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4140            $in_scope = 0;            $in_scope = 0;
4141          }          }
4142        } # INSCOPE        } # INSCOPE
4143        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4144          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4145            !!!parse-error (type => 'unmatched end tag',
4146                            text => $token->{tag_name},
4147                            token => $end_tag_token);
4148          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4149          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4150          return;          return;
4151        }        }
4152        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4153          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4154            !!!parse-error (type => 'not closed',
4155                            text => $self->{open_elements}->[-1]->[0]
4156                                ->manakai_local_name,
4157                            token => $end_tag_token);
4158        }        }
4159                
4160        ## Step 2        ## Step 2
# Line 2528  sub _tree_construction_main ($) { Line 4162  sub _tree_construction_main ($) {
4162        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4163        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4164          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4165          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4166              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4167              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4168               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4169              !!!cp ('t59');
4170            $furthest_block = $node;            $furthest_block = $node;
4171            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4172          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4173              !!!cp ('t60');
4174            last OE;            last OE;
4175          }          }
4176        } # OE        } # OE
4177                
4178        ## Step 3        ## Step 3
4179        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4180            !!!cp ('t61');
4181          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4182          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4183          !!!next-token;          !!!next-token;
# Line 2553  sub _tree_construction_main ($) { Line 4190  sub _tree_construction_main ($) {
4190        ## Step 5        ## Step 5
4191        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4192        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4193            !!!cp ('t62');
4194          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4195        }        }
4196                
# Line 2575  sub _tree_construction_main ($) { Line 4213  sub _tree_construction_main ($) {
4213          S7S2: {          S7S2: {
4214            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4215              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4216                  !!!cp ('t63');
4217                $node_i_in_active = $_;                $node_i_in_active = $_;
4218                last S7S2;                last S7S2;
4219              }              }
# Line 2588  sub _tree_construction_main ($) { Line 4227  sub _tree_construction_main ($) {
4227                    
4228          ## Step 4          ## Step 4
4229          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4230              !!!cp ('t64');
4231            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4232          }          }
4233                    
4234          ## Step 5          ## Step 5
4235          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4236              !!!cp ('t65');
4237            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4238            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4239            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2610  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        } # S7          } # S7  
4252                
4253        ## Step 8        ## Step 8
4254        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4255            my $foster_parent_element;
4256            my $next_sibling;
4257            OE: for (reverse 0..$#{$self->{open_elements}}) {
4258              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4259                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4260                                 if (defined $parent and $parent->node_type == 1) {
4261                                   !!!cp ('t65.1');
4262                                   $foster_parent_element = $parent;
4263                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4264                                 } else {
4265                                   !!!cp ('t65.2');
4266                                   $foster_parent_element
4267                                     = $self->{open_elements}->[$_ - 1]->[0];
4268                                 }
4269                                 last OE;
4270                               }
4271                             } # OE
4272                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4273                               unless defined $foster_parent_element;
4274            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4275            $open_tables->[-1]->[1] = 1; # tainted
4276          } else {
4277            !!!cp ('t65.3');
4278            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4279          }
4280                
4281        ## Step 9        ## Step 9
4282        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2627  sub _tree_construction_main ($) { Line 4293  sub _tree_construction_main ($) {
4293        my $i;        my $i;
4294        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4295          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4296              !!!cp ('t66');
4297            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4298            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4299          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4300              !!!cp ('t67');
4301            $i = $_;            $i = $_;
4302          }          }
4303        } # AFE        } # AFE
# Line 2639  sub _tree_construction_main ($) { Line 4307  sub _tree_construction_main ($) {
4307        undef $i;        undef $i;
4308        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4309          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4310              !!!cp ('t68');
4311            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4312            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4313          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4314              !!!cp ('t69');
4315            $i = $_;            $i = $_;
4316          }          }
4317        } # OE        } # OE
# Line 2652  sub _tree_construction_main ($) { Line 4322  sub _tree_construction_main ($) {
4322      } # FET      } # FET
4323    }; # $formatting_end_tag    }; # $formatting_end_tag
4324    
4325    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4326      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4327    }; # $insert_to_current    }; # $insert_to_current
4328    
4329    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4330                         my $child = shift;      my $child = shift;
4331                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4332                              table => 1, tbody => 1, tfoot => 1,        # MUST
4333                              thead => 1, tr => 1,        my $foster_parent_element;
4334                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4335                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4336                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
4337                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4338                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4339                                   !!!cp ('t70');
4340                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4341                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4342                               } else {                               } else {
4343                                   !!!cp ('t71');
4344                                 $foster_parent_element                                 $foster_parent_element
4345                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4346                               }                               }
# Line 2682  sub _tree_construction_main ($) { Line 4351  sub _tree_construction_main ($) {
4351                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4352                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4353                             ($child, $next_sibling);                             ($child, $next_sibling);
4354                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4355                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4356                         }        !!!cp ('t72');
4357          $self->{open_elements}->[-1]->[0]->append_child ($child);
4358        }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361    my $insert;    ## NOTE: When a character is inserted, if the last node that was
4362      ## inserted by the parser is a Text node and the character has to be
4363      ## inserted after that node, then the character is appended to the
4364      ## Text node.  However, if any other node is inserted by the parser,
4365      ## then a new Text node is created and the character is appended as
4366      ## that Text node.  If I'm not wrong, there are only two cases where
4367      ## this occurs.  One is the case where an element node is inserted
4368      ## to the |head| element.  This is covered by using the
4369      ## |$self->{head_element_inserted}| flag.  Another is the case where
4370      ## an element or comment is inserted into the |table| subtree while
4371      ## foster parenting happens.  This is covered by using the [2] flag
4372      ## of the |$open_tables| structure.  All other cases are handled
4373      ## simply by calling |manakai_append_text| method.
4374    
4375    B: {    B: while (1) {
4376      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4377        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
4378          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4379        ## Ignore the token        ## Ignore the token
4380        ## Stay in the phase        ## Stay in the phase
4381        !!!next-token;        !!!next-token;
4382        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         #  
       } else {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              tbody => 1, tfoot=> 1, thead => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => END_TAG_TOKEN, tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!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') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
   
       ## Stop parsing  
       last B;  
4383      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4384               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4385        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4386          ## Turn into the main phase          !!!cp ('t79');
4387          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4388          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4389        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4390          ## Turn into the main phase          !!!cp ('t80');
4391          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4392          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4393          } else {
4394            !!!cp ('t81');
4395        }        }
4396    
4397  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
4398  ## ISSUE: "<html>" in fragment is not a parse error.        !!!parse-error (type => 'not first start tag', token => $token);
       unless ($token->{first_start_tag}) {  
         !!!parse-error (type => 'not first start tag');  
       }  
4399        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4400        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4401          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4402              !!!cp ('t84');
4403            $top_el->set_attribute_ns            $top_el->set_attribute_ns
4404              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
4405               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4406          }          }
4407        }        }
4408          !!!nack ('t84.1');
4409        !!!next-token;        !!!next-token;
4410        redo B;        next B;
4411      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4412        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4413        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4414            !!!cp ('t85');
4415          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
4416        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4417            !!!cp ('t86');
4418          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
4419        } else {        } else {
4420            !!!cp ('t87');
4421          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4422            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4423        }        }
4424        !!!next-token;        !!!next-token;
4425        redo B;        next B;
4426      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4427        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4428          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t87.1');
4429            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4430            !!!next-token;
4431            next B;
4432          } elsif ($token->{type} == START_TAG_TOKEN) {
4433            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4434                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4435                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4436                ($token->{tag_name} eq 'svg' and
4437                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4438              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4439              !!!cp ('t87.2');
4440              #
4441            } elsif ({
4442                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4443                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4444                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4445                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4446                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4447                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4448                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4449                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4450                     }->{$token->{tag_name}}) {
4451              !!!cp ('t87.2');
4452              !!!parse-error (type => 'not closed',
4453                              text => $self->{open_elements}->[-1]->[0]
4454                                  ->manakai_local_name,
4455                              token => $token);
4456    
4457              pop @{$self->{open_elements}}
4458                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4459    
4460              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4461              ## Reprocess.
4462              next B;
4463            } else {
4464              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4465              my $tag_name = $token->{tag_name};
4466              if ($nsuri eq $SVG_NS) {
4467                $tag_name = {
4468                   altglyph => 'altGlyph',
4469                   altglyphdef => 'altGlyphDef',
4470                   altglyphitem => 'altGlyphItem',
4471                   animatecolor => 'animateColor',
4472                   animatemotion => 'animateMotion',
4473                   animatetransform => 'animateTransform',
4474                   clippath => 'clipPath',
4475                   feblend => 'feBlend',
4476                   fecolormatrix => 'feColorMatrix',
4477                   fecomponenttransfer => 'feComponentTransfer',
4478                   fecomposite => 'feComposite',
4479                   feconvolvematrix => 'feConvolveMatrix',
4480                   fediffuselighting => 'feDiffuseLighting',
4481                   fedisplacementmap => 'feDisplacementMap',
4482                   fedistantlight => 'feDistantLight',
4483                   feflood => 'feFlood',
4484                   fefunca => 'feFuncA',
4485                   fefuncb => 'feFuncB',
4486                   fefuncg => 'feFuncG',
4487                   fefuncr => 'feFuncR',
4488                   fegaussianblur => 'feGaussianBlur',
4489                   feimage => 'feImage',
4490                   femerge => 'feMerge',
4491                   femergenode => 'feMergeNode',
4492                   femorphology => 'feMorphology',
4493                   feoffset => 'feOffset',
4494                   fepointlight => 'fePointLight',
4495                   fespecularlighting => 'feSpecularLighting',
4496                   fespotlight => 'feSpotLight',
4497                   fetile => 'feTile',
4498                   feturbulence => 'feTurbulence',
4499                   foreignobject => 'foreignObject',
4500                   glyphref => 'glyphRef',
4501                   lineargradient => 'linearGradient',
4502                   radialgradient => 'radialGradient',
4503                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4504                   textpath => 'textPath',  
4505                }->{$tag_name} || $tag_name;
4506              }
4507    
4508              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4509    
4510              ## "adjust foreign attributes" - done in insert-element-f
4511    
4512              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4513    
4514              if ($self->{self_closing}) {
4515                pop @{$self->{open_elements}};
4516                !!!ack ('t87.3');
4517              } else {
4518                !!!cp ('t87.4');
4519              }
4520    
4521              !!!next-token;
4522              next B;
4523            }
4524          } elsif ($token->{type} == END_TAG_TOKEN) {
4525            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4526            !!!cp ('t87.5');
4527            #
4528          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4529            !!!cp ('t87.6');
4530            !!!parse-error (type => 'not closed',
4531                            text => $self->{open_elements}->[-1]->[0]
4532                                ->manakai_local_name,
4533                            token => $token);
4534    
4535            pop @{$self->{open_elements}}
4536                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4537    
4538            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4539    
4540            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4541            ## Reprocess.
4542            next B;
4543          } else {
4544            die "$0: $token->{type}: Unknown token type";        
4545          }
4546        }
4547    
4548        if ($self->{insertion_mode} & HEAD_IMS) {
4549          if ($token->{type} == CHARACTER_TOKEN) {
4550            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4551              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552                if ($self->{head_element_inserted}) {
4553                  !!!cp ('t88.3');
4554                  $self->{open_elements}->[-1]->[0]->append_child
4555                    ($self->{document}->create_text_node ($1));
4556                  delete $self->{head_element_inserted};
4557                  ## NOTE: |</head> <link> |
4558                  #
4559                } else {
4560                  !!!cp ('t88.2');
4561                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4562                  ## NOTE: |</head> &#x20;|
4563                  #
4564                }
4565              } else {
4566                !!!cp ('t88.1');
4567                ## Ignore the token.
4568                #
4569              }
4570            unless (length $token->{data}) {            unless (length $token->{data}) {
4571                !!!cp ('t88');
4572              !!!next-token;              !!!next-token;
4573              redo B;              next B;
4574            }            }
4575    ## TODO: set $token->{column} appropriately
4576          }          }
4577    
4578          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4579              !!!cp ('t89');
4580            ## As if <head>            ## As if <head>
4581            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4582            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4583            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4584                  [$self->{head_element}, $el_category->{head}];
4585    
4586            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4587            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4588    
4589            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4590          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4591              !!!cp ('t90');
4592            ## As if </noscript>            ## As if </noscript>
4593            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4594            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4595                        
4596            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4597            ## As if </head>            ## As if </head>
# Line 2793  sub _tree_construction_main ($) { Line 4599  sub _tree_construction_main ($) {
4599    
4600            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4601          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4602              !!!cp ('t91');
4603            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4604    
4605            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4606            } else {
4607              !!!cp ('t92');
4608          }          }
4609    
4610              ## "after head" insertion mode          ## "after head" insertion mode
4611              ## As if <body>          ## As if <body>
4612              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4613              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4614              ## reprocess          ## reprocess
4615              redo B;          next B;
4616            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4617              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4618                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4619                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!cp ('t93');
4620                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4621                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];              $self->{open_elements}->[-1]->[0]->append_child
4622                  $self->{insertion_mode} = IN_HEAD_IM;                  ($self->{head_element});
4623                  !!!next-token;              push @{$self->{open_elements}},
4624                  redo B;                  [$self->{head_element}, $el_category->{head}];
4625                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4626                  #              !!!nack ('t93.1');
4627                } else {              !!!next-token;
4628                  !!!parse-error (type => 'in head:head'); # or in head noscript              next B;
4629                  ## Ignore the token            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4630                  !!!next-token;              !!!cp ('t93.2');
4631                  redo B;              !!!parse-error (type => 'after head', text => 'head',
4632                }                              token => $token);
4633              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              ## Ignore the token
4634                ## As if <head>              !!!nack ('t93.3');
4635                !!!create-element ($self->{head_element}, 'head');              !!!next-token;
4636                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              next B;
4637                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4638                !!!cp ('t95');
4639                !!!parse-error (type => 'in head:head',
4640                                token => $token); # or in head noscript
4641                ## Ignore the token
4642                !!!nack ('t95.1');
4643                !!!next-token;
4644                next B;
4645              }
4646            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647              !!!cp ('t96');
4648              ## As if <head>
4649              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4650              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4651              push @{$self->{open_elements}},
4652                  [$self->{head_element}, $el_category->{head}];
4653    
4654                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4655                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4656              }          } else {
4657              !!!cp ('t97');
4658            }
4659    
4660              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4661                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4662                  ## As if </noscript>              !!!cp ('t98');
4663                  pop @{$self->{open_elements}};              ## As if </noscript>
4664                  !!!parse-error (type => 'in noscript:base');              pop @{$self->{open_elements}};
4665                              !!!parse-error (type => 'in noscript', text => 'base',
4666                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4667                  ## Reprocess in the "in head" insertion mode...            
4668                }              $self->{insertion_mode} = IN_HEAD_IM;
4669                ## Reprocess in the "in head" insertion mode...
4670              } else {
4671                !!!cp ('t99');
4672              }
4673    
4674                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4675                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4676                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t100');
4677                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4678                }                              text => $token->{tag_name}, token => $token);
4679                !!!insert-element ($token->{tag_name}, $token->{attributes});              push @{$self->{open_elements}},
4680                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  [$self->{head_element}, $el_category->{head}];
4681                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4682                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4683                !!!next-token;              !!!cp ('t101');
4684                redo B;            }
4685              } elsif ($token->{tag_name} eq 'link') {            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4686                ## NOTE: There is a "as if in head" code clone.            pop @{$self->{open_elements}};
4687                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}} # <head>
4688                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4689                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!nack ('t101.1');
4690                }            !!!next-token;
4691                !!!insert-element ($token->{tag_name}, $token->{attributes});            next B;
4692                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          } elsif ($token->{tag_name} eq 'link') {
4693                pop @{$self->{open_elements}}            ## NOTE: There is a "as if in head" code clone.
4694                    if $self->{insertion_mode} == AFTER_HEAD_IM;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4695                !!!next-token;              !!!cp ('t102');
4696                redo B;              !!!parse-error (type => 'after head',
4697              } elsif ($token->{tag_name} eq 'meta') {                              text => $token->{tag_name}, token => $token);
4698                ## NOTE: There is a "as if in head" code clone.              push @{$self->{open_elements}},
4699                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  [$self->{head_element}, $el_category->{head}];
4700                  !!!parse-error (type => 'after head:'.$token->{tag_name});              $self->{head_element_inserted} = 1;
4701                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4702                }              !!!cp ('t103');
4703                !!!insert-element ($token->{tag_name}, $token->{attributes});            }
4704                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4705              pop @{$self->{open_elements}};
4706              pop @{$self->{open_elements}} # <head>
4707                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4708              !!!ack ('t103.1');
4709              !!!next-token;
4710              next B;
4711            } elsif ($token->{tag_name} eq 'command' or
4712                     $token->{tag_name} eq 'eventsource') {
4713              if ($self->{insertion_mode} == IN_HEAD_IM) {
4714                ## NOTE: If the insertion mode at the time of the emission
4715                ## of the token was "before head", $self->{insertion_mode}
4716                ## is already changed to |IN_HEAD_IM|.
4717    
4718                ## NOTE: There is a "as if in head" code clone.
4719                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4720                pop @{$self->{open_elements}};
4721                pop @{$self->{open_elements}} # <head>
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4723                !!!ack ('t103.2');
4724                !!!next-token;
4725                next B;
4726              } else {
4727                ## NOTE: "in head noscript" or "after head" insertion mode
4728                ## - in these cases, these tags are treated as same as
4729                ## normal in-body tags.
4730                !!!cp ('t103.3');
4731                #
4732              }
4733            } elsif ($token->{tag_name} eq 'meta') {
4734              ## NOTE: There is a "as if in head" code clone.
4735              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4736                !!!cp ('t104');
4737                !!!parse-error (type => 'after head',
4738                                text => $token->{tag_name}, token => $token);
4739                push @{$self->{open_elements}},
4740                    [$self->{head_element}, $el_category->{head}];
4741                $self->{head_element_inserted} = 1;
4742              } else {
4743                !!!cp ('t105');
4744              }
4745              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4746              my $meta_el = pop @{$self->{open_elements}};
4747    
4748                unless ($self->{confident}) {                unless ($self->{confident}) {
4749                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4750                      !!!cp ('t106');
4751                      ## NOTE: Whether the encoding is supported or not is handled
4752                      ## in the {change_encoding} callback.
4753                    $self->{change_encoding}                    $self->{change_encoding}
4754                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4755                             $token);
4756                                        
4757                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4758                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4759                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4760                                                 ->{has_reference});                                                 ->{has_reference});
4761                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4762                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4763                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4764                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*=
4765                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4766                              ([^"'\x09\x0A\x0C\x0D\x20]
4767                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4768                        !!!cp ('t107');
4769                        ## NOTE: Whether the encoding is supported or not is handled
4770                        ## in the {change_encoding} callback.
4771                      $self->{change_encoding}                      $self->{change_encoding}
4772                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4773                               $token);
4774                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4775                            ->set_user_data (manakai_has_reference =>
4776                                                 $token->{attributes}->{content}
4777                                                       ->{has_reference});
4778                      } else {
4779                        !!!cp ('t108');
4780                    }                    }
4781                  }                  }
4782                } else {                } else {
4783                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
4784                      !!!cp ('t109');
4785                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4786                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4787                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4788                                                 ->{has_reference});                                                 ->{has_reference});
4789                  }                  }
4790                    if ($token->{attributes}->{content}) {
4791                      !!!cp ('t110');
4792                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4793                          ->set_user_data (manakai_has_reference =>
4794                                               $token->{attributes}->{content}
4795                                                   ->{has_reference});
4796                    }
4797                }                }
4798    
4799                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4800                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4801                  !!!ack ('t110.1');
4802                !!!next-token;                !!!next-token;
4803                redo B;                next B;
4804              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4805                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4806                  ## As if </noscript>              !!!cp ('t111');
4807                  pop @{$self->{open_elements}};              ## As if </noscript>
4808                  !!!parse-error (type => 'in noscript:title');              pop @{$self->{open_elements}};
4809                              !!!parse-error (type => 'in noscript', text => 'title',
4810                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4811                  ## Reprocess in the "in head" insertion mode...            
4812                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4813                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4814                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4815                }              !!!cp ('t112');
4816                !!!parse-error (type => 'after head',
4817                                text => $token->{tag_name}, token => $token);
4818                push @{$self->{open_elements}},
4819                    [$self->{head_element}, $el_category->{head}];
4820                $self->{head_element_inserted} = 1;
4821              } else {
4822                !!!cp ('t113');
4823              }
4824    
4825                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4826                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4827                    : $self->{open_elements}->[-1]->[0];            pop @{$self->{open_elements}} # <head>
4828                $parse_rcdata->(RCDATA_CONTENT_MODEL,                if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                                sub { $parent->append_child ($_[0]) });            next B;
4830                pop @{$self->{open_elements}}          } elsif ($token->{tag_name} eq 'style' or
4831                    if $self->{insertion_mode} == AFTER_HEAD_IM;                   $token->{tag_name} eq 'noframes') {
4832                redo B;            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4833              } elsif ($token->{tag_name} eq 'style') {            ## insertion mode IN_HEAD_IM)
4834                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## NOTE: There is a "as if in head" code clone.
4835                ## insertion mode IN_HEAD_IM)            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4836                ## NOTE: There is a "as if in head" code clone.              !!!cp ('t114');
4837                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!parse-error (type => 'after head',
4838                  !!!parse-error (type => 'after head:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
4839                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              push @{$self->{open_elements}},
4840                }                  [$self->{head_element}, $el_category->{head}];
4841                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);              $self->{head_element_inserted} = 1;
4842                pop @{$self->{open_elements}}            } else {
4843                    if $self->{insertion_mode} == AFTER_HEAD_IM;              !!!cp ('t115');
4844                redo B;            }
4845              $parse_rcdata->(CDATA_CONTENT_MODEL);
4846              pop @{$self->{open_elements}} # <head>
4847                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4848              next B;
4849              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4850                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4851                    !!!cp ('t116');
4852                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4853                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4854                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4855                    !!!nack ('t116.1');
4856                  !!!next-token;                  !!!next-token;
4857                  redo B;                  next B;
4858                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4859                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4860                    !!!parse-error (type => 'in noscript', text => 'noscript',
4861                                    token => $token);
4862                  ## Ignore the token                  ## Ignore the token
4863                    !!!nack ('t117.1');
4864                  !!!next-token;                  !!!next-token;
4865                  redo B;                  next B;
4866                } else {                } else {
4867                    !!!cp ('t118');
4868                  #                  #
4869                }                }
4870              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4871                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4872                  ## As if </noscript>              !!!cp ('t119');
4873                  pop @{$self->{open_elements}};              ## As if </noscript>
4874                  !!!parse-error (type => 'in noscript:script');              pop @{$self->{open_elements}};
4875                              !!!parse-error (type => 'in noscript', text => 'script',
4876                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4877                  ## Reprocess in the "in head" insertion mode...            
4878                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4879                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4880                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4881                }              !!!cp ('t120');
4882                !!!parse-error (type => 'after head',
4883                                text => $token->{tag_name}, token => $token);
4884                push @{$self->{open_elements}},
4885                    [$self->{head_element}, $el_category->{head}];
4886                $self->{head_element_inserted} = 1;
4887              } else {
4888                !!!cp ('t121');
4889              }
4890    
4891                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4892                $script_start_tag->($insert_to_current);            $script_start_tag->();
4893                pop @{$self->{open_elements}}            pop @{$self->{open_elements}} # <head>
4894                    if $self->{insertion_mode} == AFTER_HEAD_IM;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4895                redo B;            next B;
4896              } elsif ($token->{tag_name} eq 'body' or          } elsif ($token->{tag_name} eq 'body' or
4897                       $token->{tag_name} eq 'frameset') {                   $token->{tag_name} eq 'frameset') {
4898                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4899                    !!!cp ('t122');
4900                  ## As if </noscript>                  ## As if </noscript>
4901                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4902                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4903                                    text => $token->{tag_name}, token => $token);
4904                                    
4905                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4906                  ## As if </head>                  ## As if </head>
# Line 2984  sub _tree_construction_main ($) { Line 4908  sub _tree_construction_main ($) {
4908                                    
4909                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4910                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4911                    !!!cp ('t124');
4912                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4913                                    
4914                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4915                  } else {
4916                    !!!cp ('t125');
4917                }                }
4918    
4919                ## "after head" insertion mode                ## "after head" insertion mode
4920                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4921                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4922                    !!!cp ('t126');
4923                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
4924                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
4925                    !!!cp ('t127');
4926                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4927                } else {                } else {
4928                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4929                }                }
4930                  !!!nack ('t127.1');
4931                !!!next-token;                !!!next-token;
4932                redo B;                next B;
4933              } else {              } else {
4934                  !!!cp ('t128');
4935                #                #
4936              }              }
4937    
4938              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4939                  !!!cp ('t129');
4940                ## As if </noscript>                ## As if </noscript>
4941                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4942                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4943                                  text => $token->{tag_name}, token => $token);
4944                                
4945                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4946                ## As if </head>                ## As if </head>
# Line 3015  sub _tree_construction_main ($) { Line 4948  sub _tree_construction_main ($) {
4948    
4949                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4950              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4951                  !!!cp ('t130');
4952                ## As if </head>                ## As if </head>
4953                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4954    
4955                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4956                } else {
4957                  !!!cp ('t131');
4958              }              }
4959    
4960              ## "after head" insertion mode              ## "after head" insertion mode
4961              ## As if <body>              ## As if <body>
4962              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4963              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4964              ## reprocess              ## reprocess
4965              redo B;              !!!ack-later;
4966                next B;
4967            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4968              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4969                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4970                    !!!cp ('t132');
4971                  ## As if <head>                  ## As if <head>
4972                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4973                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4974                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4975                        [$self->{head_element}, $el_category->{head}];
4976    
4977                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4978                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4979                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4980                  !!!next-token;                  !!!next-token;
4981                  redo B;                  next B;
4982                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4983                    !!!cp ('t133');
4984                  ## As if </noscript>                  ## As if </noscript>
4985                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4986                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/',
4987                                    text => 'head', token => $token);
4988                                    
4989                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4990                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4991                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4992                  !!!next-token;                  !!!next-token;
4993                  redo B;                  next B;
4994                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4995                    !!!cp ('t134');
4996                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4997                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4998                  !!!next-token;                  !!!next-token;
4999                  redo B;                  next B;
5000                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5001                    !!!cp ('t134.1');
5002                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5003                                    token => $token);
5004                    ## Ignore the token
5005                    !!!next-token;
5006                    next B;
5007                } else {                } else {
5008                  #                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5009                }                }
5010              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5011                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5012                    !!!cp ('t136');
5013                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5014                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5015                  !!!next-token;                  !!!next-token;
5016                  redo B;                  next B;
5017                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5018                  !!!parse-error (type => 'unmatched end tag:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
5019                    !!!cp ('t137');
5020                    !!!parse-error (type => 'unmatched end tag',
5021                                    text => 'noscript', token => $token);
5022                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5023                  !!!next-token;                  !!!next-token;
5024                  redo B;                  next B;
5025                } else {                } else {
5026                    !!!cp ('t138');
5027                  #                  #
5028                }                }
5029              } elsif ({              } elsif ({
5030                        body => 1, html => 1,                        body => 1, html => 1,
5031                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5032                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5033                  ## As if <head>                    $self->{insertion_mode} == IN_HEAD_IM or
5034                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5035                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  !!!cp ('t140');
5036                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'unmatched end tag',
5037                                    text => $token->{tag_name}, token => $token);
                 $self->{insertion_mode} = IN_HEAD_IM;  
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5038                  ## Ignore the token                  ## Ignore the token
5039                  !!!next-token;                  !!!next-token;
5040                  redo B;                  next B;
5041                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5042                    !!!cp ('t140.1');
5043                    !!!parse-error (type => 'unmatched end tag',
5044                                    text => $token->{tag_name}, token => $token);
5045                    ## Ignore the token
5046                    !!!next-token;
5047                    next B;
5048                  } else {
5049                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5050                }                }
5051                              } elsif ($token->{tag_name} eq 'p') {
5052                #                !!!cp ('t142');
5053              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5054                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5055                       }->{$token->{tag_name}}) {                ## Ignore the token
5056                  !!!next-token;
5057                  next B;
5058                } elsif ($token->{tag_name} eq 'br') {
5059                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5060                  ## As if <head>                  !!!cp ('t142.2');
5061                  !!!create-element ($self->{head_element}, 'head');                  ## (before head) as if <head>, (in head) as if </head>
5062                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5063                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5064                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5065      
5066                    ## Reprocess in the "after head" insertion mode...
5067                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5068                    !!!cp ('t143.2');
5069                    ## As if </head>
5070                    pop @{$self->{open_elements}};
5071                    $self->{insertion_mode} = AFTER_HEAD_IM;
5072      
5073                    ## Reprocess in the "after head" insertion mode...
5074                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5075                    !!!cp ('t143.3');
5076                    ## ISSUE: Two parse errors for <head><noscript></br>
5077                    !!!parse-error (type => 'unmatched end tag',
5078                                    text => 'br', token => $token);
5079                    ## As if </noscript>
5080                    pop @{$self->{open_elements}};
5081                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5082    
5083                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5084                }                  ## As if </head>
5085                    pop @{$self->{open_elements}};
5086                    $self->{insertion_mode} = AFTER_HEAD_IM;
5087    
5088                #                  ## Reprocess in the "after head" insertion mode...
5089              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5090                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
5091                  #                  #
5092                } else {                } else {
5093                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5094                }                }
5095    
5096                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5097                  !!!parse-error (type => 'unmatched end tag',
5098                                  text => 'br', token => $token);
5099                  ## Ignore the token
5100                  !!!next-token;
5101                  next B;
5102                } else {
5103                  !!!cp ('t145');
5104                  !!!parse-error (type => 'unmatched end tag',
5105                                  text => $token->{tag_name}, token => $token);
5106                  ## Ignore the token
5107                  !!!next-token;
5108                  next B;
5109              }              }
5110    
5111              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5112                  !!!cp ('t146');
5113                ## As if </noscript>                ## As if </noscript>
5114                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5115                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5116                                  text => $token->{tag_name}, token => $token);
5117                                
5118                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5119                ## As if </head>                ## As if </head>
# Line 3127  sub _tree_construction_main ($) { Line 5121  sub _tree_construction_main ($) {
5121    
5122                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5123              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5124                  !!!cp ('t147');
5125                ## As if </head>                ## As if </head>
5126                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5127    
5128                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5129              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5130                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
5131                  !!!cp ('t148');
5132                  !!!parse-error (type => 'unmatched end tag',
5133                                  text => $token->{tag_name}, token => $token);
5134                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5135                !!!next-token;                !!!next-token;
5136                redo B;                next B;
5137                } else {
5138                  !!!cp ('t149');
5139              }              }
5140    
5141              ## "after head" insertion mode              ## "after head" insertion mode
5142              ## As if <body>              ## As if <body>
5143              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5144              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5145              ## reprocess              ## reprocess
5146              redo B;              next B;
5147            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5148              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5149            }            !!!cp ('t149.1');
5150    
5151              ## NOTE: As if <head>
5152              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5153              $self->{open_elements}->[-1]->[0]->append_child
5154                  ($self->{head_element});
5155              #push @{$self->{open_elements}},
5156              #    [$self->{head_element}, $el_category->{head}];
5157              #$self->{insertion_mode} = IN_HEAD_IM;
5158              ## NOTE: Reprocess.
5159    
5160              ## NOTE: As if </head>
5161              #pop @{$self->{open_elements}};
5162              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5163              ## NOTE: Reprocess.
5164              
5165              #
5166            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5167              !!!cp ('t149.2');
5168    
5169            ## ISSUE: An issue in the spec.            ## NOTE: As if </head>
5170              pop @{$self->{open_elements}};
5171              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5172              ## NOTE: Reprocess.
5173    
5174              #
5175            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5176              !!!cp ('t149.3');
5177    
5178              !!!parse-error (type => 'in noscript:#eof', token => $token);
5179    
5180              ## As if </noscript>
5181              pop @{$self->{open_elements}};
5182              #$self->{insertion_mode} = IN_HEAD_IM;
5183              ## NOTE: Reprocess.
5184    
5185              ## NOTE: As if </head>
5186              pop @{$self->{open_elements}};
5187              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5188              ## NOTE: Reprocess.
5189    
5190              #
5191            } else {
5192              !!!cp ('t149.4');
5193              #
5194            }
5195    
5196            ## NOTE: As if <body>
5197            !!!insert-element ('body',, $token);
5198            $self->{insertion_mode} = IN_BODY_IM;
5199            ## NOTE: Reprocess.
5200            next B;
5201          } else {
5202            die "$0: $token->{type}: Unknown token type";
5203          }
5204      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5205            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5206                !!!cp ('t150');
5207              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5208              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5209                            
5210              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5211    
5212              !!!next-token;              !!!next-token;
5213              redo B;              next B;
5214            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5215              if ({              if ({
5216                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3165  sub _tree_construction_main ($) { Line 5218  sub _tree_construction_main ($) {
5218                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5219                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5220                  ## have an element in table scope                  ## have an element in table scope
5221                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5222                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5223                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5224                      $tn = $node->[1];                      !!!cp ('t151');
5225                      last INSCOPE;  
5226                    } elsif ({                      ## Close the cell
5227                              table => 1, html => 1,                      !!!back-token; # <x>
5228                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5229                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5230                    }                                line => $token->{line},
5231                  } # INSCOPE                                column => $token->{column}};
5232                    unless (defined $tn) {                      next B;
5233                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5234                      ## Ignore the token                      !!!cp ('t152');
5235                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5236                      redo B;                      last;
5237                    }                    }
5238                                    }
5239                  ## Close the cell  
5240                  !!!back-token; # <?>                  !!!cp ('t153');
5241                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
5242                  redo B;                      text => $token->{tag_name}, token => $token);
5243                    ## Ignore the token
5244                    !!!nack ('t153.1');
5245                    !!!next-token;
5246                    next B;
5247                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5248                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5249                                    token => $token);
5250                                    
5251                  ## As if </caption>                  ## NOTE: As if </caption>.
5252                  ## have a table element in table scope                  ## have a table element in table scope
5253                  my $i;                  my $i;
5254                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5255                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5256                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5257                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5258                      last INSCOPE;                        !!!cp ('t155');
5259                    } elsif ({                        $i = $_;
5260                              table => 1, html => 1,                        last INSCOPE;
5261                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5262                      last INSCOPE;                        !!!cp ('t156');
5263                          last;
5264                        }
5265                    }                    }
5266    
5267                      !!!cp ('t157');
5268                      !!!parse-error (type => 'start tag not allowed',
5269                                      text => $token->{tag_name}, token => $token);
5270                      ## Ignore the token
5271                      !!!nack ('t157.1');
5272                      !!!next-token;
5273                      next B;
5274                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5275                                    
5276                  ## generate implied end tags                  ## generate implied end tags
5277                  if ({                  while ($self->{open_elements}->[-1]->[1]
5278                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5279                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5280                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token; # <?>  
                   $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5281                  }                  }
5282    
5283                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5284                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5285                      !!!parse-error (type => 'not closed',
5286                                      text => $self->{open_elements}->[-1]->[0]
5287                                          ->manakai_local_name,
5288                                      token => $token);
5289                    } else {
5290                      !!!cp ('t160');
5291                  }                  }
5292                                    
5293                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3237  sub _tree_construction_main ($) { Line 5297  sub _tree_construction_main ($) {
5297                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5298                                    
5299                  ## reprocess                  ## reprocess
5300                  redo B;                  !!!ack-later;
5301                    next B;
5302                } else {                } else {
5303                    !!!cp ('t161');
5304                  #                  #
5305                }                }
5306              } else {              } else {
5307                  !!!cp ('t162');
5308                #                #
5309              }              }
5310            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3251  sub _tree_construction_main ($) { Line 5314  sub _tree_construction_main ($) {
5314                  my $i;                  my $i;
5315                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5316                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5317                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5318                        !!!cp ('t163');
5319                      $i = $_;                      $i = $_;
5320                      last INSCOPE;                      last INSCOPE;
5321                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5322                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5323                      last INSCOPE;                      last INSCOPE;
5324                    }                    }
5325                  } # INSCOPE                  } # INSCOPE
5326                    unless (defined $i) {                    unless (defined $i) {
5327                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5328                        !!!parse-error (type => 'unmatched end tag',
5329                                        text => $token->{tag_name},
5330                                        token => $token);
5331                      ## Ignore the token                      ## Ignore the token
5332                      !!!next-token;                      !!!next-token;
5333                      redo B;                      next B;
5334                    }                    }
5335                                    
5336                  ## generate implied end tags                  ## generate implied end tags
5337                  if ({                  while ($self->{open_elements}->[-1]->[1]
5338                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5339                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5340                       th => ($token->{tag_name} eq 'td'),                    pop @{$self->{open_elements}};
                      tr => 1,  
                      tbody => 1, tfoot=> 1, thead => 1,  
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5341                  }                  }
5342                    
5343                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5344                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5345                      !!!cp ('t167');
5346                      !!!parse-error (type => 'not closed',
5347                                      text => $self->{open_elements}->[-1]->[0]
5348                                          ->manakai_local_name,
5349                                      token => $token);
5350                    } else {
5351                      !!!cp ('t168');
5352                  }                  }
5353                                    
5354                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3292  sub _tree_construction_main ($) { Line 5358  sub _tree_construction_main ($) {
5358                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5359                                    
5360                  !!!next-token;                  !!!next-token;
5361                  redo B;                  next B;
5362                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5363                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5364                    !!!parse-error (type => 'unmatched end tag',
5365                                    text => $token->{tag_name}, token => $token);
5366                  ## Ignore the token                  ## Ignore the token
5367                  !!!next-token;                  !!!next-token;
5368                  redo B;                  next B;
5369                } else {                } else {
5370                    !!!cp ('t170');
5371                  #                  #
5372                }                }
5373              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5374                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5375                  ## have a table element in table scope                  ## have a table element in table scope
5376                  my $i;                  my $i;
5377                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5378                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5379                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5380                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5381                      last INSCOPE;                        !!!cp ('t171');
5382                    } elsif ({                        $i = $_;
5383                              table => 1, html => 1,                        last INSCOPE;
5384                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5385                      last INSCOPE;                        !!!cp ('t172');
5386                          last;
5387                        }
5388                    }                    }
5389    
5390                      !!!cp ('t173');
5391                      !!!parse-error (type => 'unmatched end tag',
5392                                      text => $token->{tag_name}, token => $token);
5393                      ## Ignore the token
5394                      !!!next-token;
5395                      next B;
5396                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5397                                    
5398                  ## generate implied end tags                  ## generate implied end tags
5399                  if ({                  while ($self->{open_elements}->[-1]->[1]
5400                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5401                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5402                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => END_TAG_TOKEN,  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5403                  }                  }
5404                                    
5405                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5406                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5407                      !!!parse-error (type => 'not closed',
5408                                      text => $self->{open_elements}->[-1]->[0]
5409                                          ->manakai_local_name,
5410                                      token => $token);
5411                    } else {
5412                      !!!cp ('t176');
5413                  }                  }
5414                                    
5415                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3346  sub _tree_construction_main ($) { Line 5419  sub _tree_construction_main ($) {
5419                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5420                                    
5421                  !!!next-token;                  !!!next-token;
5422                  redo B;                  next B;
5423                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5424                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5425                    !!!parse-error (type => 'unmatched end tag',
5426                                    text => $token->{tag_name}, token => $token);
5427                  ## Ignore the token                  ## Ignore the token
5428                  !!!next-token;                  !!!next-token;
5429                  redo B;                  next B;
5430                } else {                } else {
5431                    !!!cp ('t178');
5432                  #                  #
5433                }                }
5434              } elsif ({              } elsif ({
# Line 3363  sub _tree_construction_main ($) { Line 5439  sub _tree_construction_main ($) {
5439                ## have an element in table scope                ## have an element in table scope
5440                my $i;                my $i;
5441                my $tn;                my $tn;
5442                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5443                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5444                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5445                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5446                    last INSCOPE;                      !!!cp ('t179');
5447                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5448                    $tn = $node->[1];  
5449                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5450                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5451                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5452                            table => 1, html => 1,                                line => $token->{line},
5453                           }->{$node->[1]}) {                                column => $token->{column}};
5454                    last INSCOPE;                      next B;
5455                      } elsif ($node->[1] & TABLE_CELL_EL) {
5456                        !!!cp ('t180');
5457                        $tn = $node->[0]->manakai_local_name;
5458                        ## NOTE: There is exactly one |td| or |th| element
5459                        ## in scope in the stack of open elements by definition.
5460                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5461                        ## ISSUE: Can this be reached?
5462                        !!!cp ('t181');
5463                        last;
5464                      }
5465                  }                  }
5466                } # INSCOPE  
5467                unless (defined $i) {                  !!!cp ('t182');
5468                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5469                        text => $token->{tag_name}, token => $token);
5470                  ## Ignore the token                  ## Ignore the token
5471                  !!!next-token;                  !!!next-token;
5472                  redo B;                  next B;
5473                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5474              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5475                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5476                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5477                                  token => $token);
5478    
5479                ## As if </caption>                ## As if </caption>
5480                ## have a table element in table scope                ## have a table element in table scope
5481                my $i;                my $i;
5482                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5483                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5484                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5485                      !!!cp ('t184');
5486                    $i = $_;                    $i = $_;
5487                    last INSCOPE;                    last INSCOPE;
5488                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5489                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5490                    last INSCOPE;                    last INSCOPE;
5491                  }                  }
5492                } # INSCOPE                } # INSCOPE
5493                unless (defined $i) {                unless (defined $i) {
5494                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5495                    !!!parse-error (type => 'unmatched end tag',
5496                                    text => 'caption', token => $token);
5497                  ## Ignore the token                  ## Ignore the token
5498                  !!!next-token;                  !!!next-token;
5499                  redo B;                  next B;
5500                }                }
5501                                
5502                ## generate implied end tags                ## generate implied end tags
5503                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5504                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5505                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5506                }                }
5507    
5508                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5509                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5510                    !!!parse-error (type => 'not closed',
5511                                    text => $self->{open_elements}->[-1]->[0]
5512                                        ->manakai_local_name,
5513                                    token => $token);
5514                  } else {
5515                    !!!cp ('t189');
5516                }                }
5517    
5518                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3439  sub _tree_construction_main ($) { Line 5522  sub _tree_construction_main ($) {
5522                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5523    
5524                ## reprocess                ## reprocess
5525                redo B;                next B;
5526              } elsif ({              } elsif ({
5527                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5528                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5529                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5530                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
5531                    !!!parse-error (type => 'unmatched end tag',
5532                                    text => $token->{tag_name}, token => $token);
5533                  ## Ignore the token                  ## Ignore the token
5534                  !!!next-token;                  !!!next-token;
5535                  redo B;                  next B;
5536                } else {                } else {
5537                    !!!cp ('t191');
5538                  #                  #
5539                }                }
5540              } elsif ({              } elsif ({
# Line 3456  sub _tree_construction_main ($) { Line 5542  sub _tree_construction_main ($) {
5542                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5543                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5544                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5545                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5546                  !!!parse-error (type => 'unmatched end tag',
5547                                  text => $token->{tag_name}, token => $token);
5548                ## Ignore the token                ## Ignore the token
5549                !!!next-token;                !!!next-token;
5550                redo B;                next B;
5551              } else {              } else {
5552                  !!!cp ('t193');
5553                #                #
5554              }              }
5555          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5556            for my $entry (@{$self->{open_elements}}) {
5557              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5558                !!!cp ('t75');
5559                !!!parse-error (type => 'in body:#eof', token => $token);
5560                last;
5561              }
5562            }
5563    
5564            ## Stop parsing.
5565            last B;
5566        } else {        } else {
5567          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5568        }        }
# Line 3471  sub _tree_construction_main ($) { Line 5571  sub _tree_construction_main ($) {
5571        #        #
5572      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5573        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5574              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5575                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5576              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5577                                
5578                unless (length $token->{data}) {            unless (length $token->{data}) {
5579                  !!!next-token;              !!!cp ('t194');
5580                  redo B;              !!!next-token;
5581                }              next B;
5582              }            } else {
5583                !!!cp ('t195');
5584              }
5585            }
5586    
5587              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5588    
5589              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5590              ## 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);  
5591                            
5592              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5593                   table => 1, tbody => 1, tfoot => 1,            # MUST
5594                   thead => 1, tr => 1,            my $foster_parent_element;
5595                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5596                # MUST            my $prev_sibling;
5597                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5598                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5599                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5600                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5601                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5602                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5603                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5604                      $foster_parent_element = $parent;                  $prev_sibling = $next_sibling->previous_sibling;
5605                      $next_sibling = $self->{open_elements}->[$_]->[0];                  #
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $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) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
5606                } else {                } else {
5607                  $foster_parent_element->insert_before                  !!!cp ('t197');
5608                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5609                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5610                    #
5611                }                }
5612              } else {                last OE;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5613              }              }
5614              } # OE
5615              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5616              $prev_sibling = $foster_parent_element->last_child
5617                  unless defined $foster_parent_element;
5618              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5619              if (defined $prev_sibling and
5620                  $prev_sibling->node_type == 3) {
5621                !!!cp ('t198');
5622                $prev_sibling->manakai_append_text ($token->{data});
5623              } else {
5624                !!!cp ('t199');
5625                $foster_parent_element->insert_before
5626                    ($self->{document}->create_text_node ($token->{data}),
5627                     $next_sibling);
5628              }
5629              $open_tables->[-1]->[1] = 1; # tainted
5630              $open_tables->[-1]->[2] = 1; # ~node inserted
5631            } else {
5632              ## NOTE: Fragment case or in a foster parent'ed element
5633              ## (e.g. |<table><span>a|).  In fragment case, whether the
5634              ## character is appended to existing node or a new node is
5635              ## created is irrelevant, since the foster parent'ed nodes
5636              ## are discarded and fragment parsing does not invoke any
5637              ## script.
5638              !!!cp ('t200');
5639              $self->{open_elements}->[-1]->[0]->manakai_append_text
5640                  ($token->{data});
5641            }
5642                            
5643              !!!next-token;          !!!next-token;
5644              redo B;          next B;
5645        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5646              if ({          if ({
5647                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5648                   th => 1, td => 1,               th => 1, td => 1,
5649                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5650                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5651                  ## Clear back to table context              ## Clear back to table context
5652                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5653                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5654                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5655                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5656                  }              }
5657                                
5658                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5659                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5660                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5661                }            }
5662              
5663                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5664                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5665                    !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t202');
5666                  }                !!!parse-error (type => 'missing start tag:tr', token => $token);
5667                }
5668                                    
5669                  ## Clear back to table body context              ## Clear back to table body context
5670                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5671                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5672                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5673                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## ISSUE: Can this case be reached?
5674                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5675                  }              }
5676                                    
5677                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5678                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5679                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t204');
5680                    !!!next-token;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5681                    redo B;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5682                  } else {                !!!nack ('t204');
5683                    !!!insert-element ('tr');                !!!next-token;
5684                    ## reprocess in the "in row" insertion mode                next B;
5685                  }              } else {
5686                }                !!!cp ('t205');
5687                  !!!insert-element ('tr',, $token);
5688                  ## reprocess in the "in row" insertion mode
5689                }
5690              } else {
5691                !!!cp ('t206');
5692              }
5693    
5694                ## Clear back to table row context                ## Clear back to table row context
5695                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5696                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5697                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5698                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5699                }                }
5700                                
5701                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703              $self->{insertion_mode} = IN_CELL_IM;
5704    
5705                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5706                                
5707              !!!nack ('t207.1');
5708              !!!next-token;
5709              next B;
5710            } elsif ({
5711                      caption => 1, col => 1, colgroup => 1,
5712                      tbody => 1, tfoot => 1, thead => 1,
5713                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5714                     }->{$token->{tag_name}}) {
5715              if ($self->{insertion_mode} == IN_ROW_IM) {
5716                ## As if </tr>
5717                ## have an element in table scope
5718                my $i;
5719                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5720                  my $node = $self->{open_elements}->[$_];
5721                  if ($node->[1] & TABLE_ROW_EL) {
5722                    !!!cp ('t208');
5723                    $i = $_;
5724                    last INSCOPE;
5725                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5726                    !!!cp ('t209');
5727                    last INSCOPE;
5728                  }
5729                } # INSCOPE
5730                unless (defined $i) {
5731                  !!!cp ('t210');
5732                  ## TODO: This type is wrong.
5733                  !!!parse-error (type => 'unmacthed end tag',
5734                                  text => $token->{tag_name}, token => $token);
5735                  ## Ignore the token
5736                  !!!nack ('t210.1');
5737                !!!next-token;                !!!next-token;
5738                redo B;                next B;
5739              } 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') {  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ({  
                             table => 1, html => 1,  
                            }->{$node->[1]}) {  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});  
                   ## Ignore the token  
                   !!!next-token;  
                   redo B;  
                 }  
5740                                    
5741                  ## Clear back to table row context                  ## Clear back to table row context
5742                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5743                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5744                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
5745                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
5746                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5747                  }                  }
5748                                    
5749                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
5750                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5751                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5752                      !!!cp ('t212');
5753                    ## reprocess                    ## reprocess
5754                    redo B;                    !!!ack-later;
5755                      next B;
5756                  } else {                  } else {
5757                      !!!cp ('t213');
5758                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
5759                  }                  }
5760                }                }
# Line 3634  sub _tree_construction_main ($) { Line 5764  sub _tree_construction_main ($) {
5764                  my $i;                  my $i;
5765                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5766                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5767                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
5768                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
5769                      $i = $_;                      $i = $_;
5770                      last INSCOPE;                      last INSCOPE;
5771                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5772                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
5773                      last INSCOPE;                      last INSCOPE;
5774                    }                    }
5775                  } # INSCOPE                  } # INSCOPE
5776                  unless (defined $i) {                  unless (defined $i) {
5777                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
5778    ## TODO: This erorr type is wrong.
5779                      !!!parse-error (type => 'unmatched end tag',
5780                                      text => $token->{tag_name}, token => $token);
5781                    ## Ignore the token                    ## Ignore the token
5782                      !!!nack ('t216.1');
5783                    !!!next-token;                    !!!next-token;
5784                    redo B;                    next B;
5785                  }                  }
5786    
5787                  ## Clear back to table body context                  ## Clear back to table body context
5788                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5789                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5790                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5791                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5792                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5793                  }                  }
5794                                    
# Line 3670  sub _tree_construction_main ($) { Line 5802  sub _tree_construction_main ($) {
5802                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5803                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5804                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
5805                  } else {
5806                    !!!cp ('t218');
5807                }                }
5808    
5809                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5810                  ## Clear back to table context              ## Clear back to table context
5811                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5812                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5813                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t219');
5814                    pop @{$self->{open_elements}};                ## ISSUE: Can this state be reached?
5815                  }                pop @{$self->{open_elements}};
5816                                }
5817                  !!!insert-element ('colgroup');              
5818                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              !!!insert-element ('colgroup',, $token);
5819                  ## reprocess              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5820                  redo B;              ## reprocess
5821                } elsif ({              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5822                          caption => 1,              !!!ack-later;
5823                          colgroup => 1,              next B;
5824                          tbody => 1, tfoot => 1, thead => 1,            } elsif ({
5825                         }->{$token->{tag_name}}) {                      caption => 1,
5826                  ## Clear back to table context                      colgroup => 1,
5827                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                      tbody => 1, tfoot => 1, thead => 1,
5828                         $self->{open_elements}->[-1]->[1] ne 'html') {                     }->{$token->{tag_name}}) {
5829                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## Clear back to table context
5830                    while (not ($self->{open_elements}->[-1]->[1]
5831                                    & TABLE_SCOPING_EL)) {
5832                      !!!cp ('t220');
5833                      ## ISSUE: Can this state be reached?
5834                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5835                  }                  }
5836                                    
5837                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5838                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5839                                    
5840                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5841                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5842                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5843                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5844                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5845                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5846                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5847                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5848                  !!!next-token;                                        }->{$token->{tag_name}};
5849                  redo B;              !!!next-token;
5850                } else {              !!!nack ('t220.1');
5851                  die "$0: in table: <>: $token->{tag_name}";              next B;
5852                }            } else {
5853                die "$0: in table: <>: $token->{tag_name}";
5854              }
5855              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5856                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5857                                  text => $self->{open_elements}->[-1]->[0]
5858                                      ->manakai_local_name,
5859                                  token => $token);
5860    
5861                ## As if </table>                ## As if </table>
5862                ## have a table element in table scope                ## have a table element in table scope
5863                my $i;                my $i;
5864                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5865                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5866                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5867                      !!!cp ('t221');
5868                    $i = $_;                    $i = $_;
5869                    last INSCOPE;                    last INSCOPE;
5870                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5871                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5872                    last INSCOPE;                    last INSCOPE;
5873                  }                  }
5874                } # INSCOPE                } # INSCOPE
5875                unless (defined $i) {                unless (defined $i) {
5876                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5877    ## TODO: The following is wrong, maybe.
5878                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5879                                    token => $token);
5880                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5881                    !!!nack ('t223.1');
5882                  !!!next-token;                  !!!next-token;
5883                  redo B;                  next B;
5884                }                }
5885                                
5886    ## TODO: Followings are removed from the latest spec.
5887                ## generate implied end tags                ## generate implied end tags
5888                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5889                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5890                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => END_TAG_TOKEN, tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5891                }                }
5892    
5893                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5894                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5895                    ## NOTE: |<table><tr><table>|
5896                    !!!parse-error (type => 'not closed',
5897                                    text => $self->{open_elements}->[-1]->[0]
5898                                        ->manakai_local_name,
5899                                    token => $token);
5900                  } else {
5901                    !!!cp ('t226');
5902                }                }
5903    
5904                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5905                  pop @{$open_tables};
5906    
5907                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5908    
5909                ## reprocess            ## reprocess
5910                redo B;            !!!ack-later;
5911          } else {            next B;
5912            !!!parse-error (type => 'in table:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'style') {
5913              if (not $open_tables->[-1]->[1]) { # tainted
5914                !!!cp ('t227.8');
5915                ## NOTE: This is a "as if in head" code clone.
5916                $parse_rcdata->(CDATA_CONTENT_MODEL);
5917                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5918                next B;
5919              } else {
5920                !!!cp ('t227.7');
5921                #
5922              }
5923            } elsif ($token->{tag_name} eq 'script') {
5924              if (not $open_tables->[-1]->[1]) { # tainted
5925                !!!cp ('t227.6');
5926                ## NOTE: This is a "as if in head" code clone.
5927                $script_start_tag->();
5928                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5929                next B;
5930              } else {
5931                !!!cp ('t227.5');
5932                #
5933              }
5934            } elsif ($token->{tag_name} eq 'input') {
5935              if (not $open_tables->[-1]->[1]) { # tainted
5936                if ($token->{attributes}->{type}) { ## TODO: case
5937                  my $type = lc $token->{attributes}->{type}->{value};
5938                  if ($type eq 'hidden') {
5939                    !!!cp ('t227.3');
5940                    !!!parse-error (type => 'in table',
5941                                    text => $token->{tag_name}, token => $token);
5942    
5943            $insert = $insert_to_foster;                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5944                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5945    
5946                    ## TODO: form element pointer
5947    
5948                    pop @{$self->{open_elements}};
5949    
5950                    !!!next-token;
5951                    !!!ack ('t227.2.1');
5952                    next B;
5953                  } else {
5954                    !!!cp ('t227.2');
5955                    #
5956                  }
5957                } else {
5958                  !!!cp ('t227.1');
5959                  #
5960                }
5961              } else {
5962                !!!cp ('t227.4');
5963                #
5964              }
5965            } else {
5966              !!!cp ('t227');
5967            #            #
5968          }          }
5969    
5970            !!!parse-error (type => 'in table', text => $token->{tag_name},
5971                            token => $token);
5972    
5973            $insert = $insert_to_foster;
5974            #
5975        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5976              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
5977                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 3773  sub _tree_construction_main ($) { Line 5979  sub _tree_construction_main ($) {
5979                my $i;                my $i;
5980                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5981                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5982                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
5983                      !!!cp ('t228');
5984                    $i = $_;                    $i = $_;
5985                    last INSCOPE;                    last INSCOPE;
5986                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5987                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5988                    last INSCOPE;                    last INSCOPE;
5989                  }                  }
5990                } # INSCOPE                } # INSCOPE
5991                unless (defined $i) {                unless (defined $i) {
5992                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
5993                    !!!parse-error (type => 'unmatched end tag',
5994                                    text => $token->{tag_name}, token => $token);
5995                  ## Ignore the token                  ## Ignore the token
5996                    !!!nack ('t230.1');
5997                  !!!next-token;                  !!!next-token;
5998                  redo B;                  next B;
5999                  } else {
6000                    !!!cp ('t232');
6001                }                }
6002    
6003                ## Clear back to table row context                ## Clear back to table row context
6004                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6005                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6006                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6007                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6008                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6009                }                }
6010    
6011                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6012                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6013                !!!next-token;                !!!next-token;
6014                redo B;                !!!nack ('t231.1');
6015                  next B;
6016              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6017                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6018                  ## As if </tr>                  ## As if </tr>
# Line 3808  sub _tree_construction_main ($) { Line 6020  sub _tree_construction_main ($) {
6020                  my $i;                  my $i;
6021                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6022                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6023                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6024                        !!!cp ('t233');
6025                      $i = $_;                      $i = $_;
6026                      last INSCOPE;                      last INSCOPE;
6027                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6028                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
6029                      last INSCOPE;                      last INSCOPE;
6030                    }                    }
6031                  } # INSCOPE                  } # INSCOPE
6032                  unless (defined $i) {                  unless (defined $i) {
6033                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
6034    ## TODO: The following is wrong.
6035                      !!!parse-error (type => 'unmatched end tag',
6036                                      text => $token->{type}, token => $token);
6037                    ## Ignore the token                    ## Ignore the token
6038                      !!!nack ('t236.1');
6039                    !!!next-token;                    !!!next-token;
6040                    redo B;                    next B;
6041                  }                  }
6042                                    
6043                  ## Clear back to table row context                  ## Clear back to table row context
6044                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6045                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6046                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
6047                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6048                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6049                  }                  }
6050                                    
# Line 3842  sub _tree_construction_main ($) { Line 6058  sub _tree_construction_main ($) {
6058                  my $i;                  my $i;
6059                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6060                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6061                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6062                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
6063                      $i = $_;                      $i = $_;
6064                      last INSCOPE;                      last INSCOPE;
6065                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6066                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
6067                      last INSCOPE;                      last INSCOPE;
6068                    }                    }
6069                  } # INSCOPE                  } # INSCOPE
6070                  unless (defined $i) {                  unless (defined $i) {
6071                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
6072                      !!!parse-error (type => 'unmatched end tag',
6073                                      text => $token->{tag_name}, token => $token);
6074                    ## Ignore the token                    ## Ignore the token
6075                      !!!nack ('t239.1');
6076                    !!!next-token;                    !!!next-token;
6077                    redo B;                    next B;
6078                  }                  }
6079                                    
6080                  ## Clear back to table body context                  ## Clear back to table body context
6081                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6082                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
6083                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6084                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6085                  }                  }
6086                                    
# Line 3880  sub _tree_construction_main ($) { Line 6096  sub _tree_construction_main ($) {
6096                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6097                }                }
6098    
6099                  ## NOTE: </table> in the "in table" insertion mode.
6100                  ## When you edit the code fragment below, please ensure that
6101                  ## the code for <table> in the "in table" insertion mode
6102                  ## is synced with it.
6103    
6104                ## have a table element in table scope                ## have a table element in table scope
6105                my $i;                my $i;
6106                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6107                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6108                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6109                      !!!cp ('t241');
6110                    $i = $_;                    $i = $_;
6111                    last INSCOPE;                    last INSCOPE;
6112                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6113                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6114                    last INSCOPE;                    last INSCOPE;
6115                  }                  }
6116                } # INSCOPE                } # INSCOPE
6117                unless (defined $i) {                unless (defined $i) {
6118                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6119                    !!!parse-error (type => 'unmatched end tag',
6120                                    text => $token->{tag_name}, token => $token);
6121                  ## Ignore the token                  ## Ignore the token
6122                    !!!nack ('t243.1');
6123                  !!!next-token;                  !!!next-token;
6124                  redo B;                  next B;
               }  
   
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => END_TAG_TOKEN,  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
                 
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6125                }                }
6126                                    
6127                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6128                  pop @{$open_tables};
6129                                
6130                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6131                                
6132                !!!next-token;                !!!next-token;
6133                redo B;                next B;
6134              } elsif ({              } elsif ({
6135                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6136                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 3931  sub _tree_construction_main ($) { Line 6140  sub _tree_construction_main ($) {
6140                  my $i;                  my $i;
6141                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6142                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6143                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6144                        !!!cp ('t247');
6145                      $i = $_;                      $i = $_;
6146                      last INSCOPE;                      last INSCOPE;
6147                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6148                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
6149                      last INSCOPE;                      last INSCOPE;
6150                    }                    }
6151                  } # INSCOPE                  } # INSCOPE
6152                    unless (defined $i) {                    unless (defined $i) {
6153                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
6154                        !!!parse-error (type => 'unmatched end tag',
6155                                        text => $token->{tag_name}, token => $token);
6156                      ## Ignore the token                      ## Ignore the token
6157                        !!!nack ('t249.1');
6158                      !!!next-token;                      !!!next-token;
6159                      redo B;                      next B;
6160                    }                    }
6161                                    
6162                  ## As if </tr>                  ## As if </tr>
# Line 3952  sub _tree_construction_main ($) { Line 6164  sub _tree_construction_main ($) {
6164                  my $i;                  my $i;
6165                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6166                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6167                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6168                        !!!cp ('t250');
6169                      $i = $_;                      $i = $_;
6170                      last INSCOPE;                      last INSCOPE;
6171                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6172                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
6173                      last INSCOPE;                      last INSCOPE;
6174                    }                    }
6175                  } # INSCOPE                  } # INSCOPE
6176                    unless (defined $i) {                    unless (defined $i) {
6177                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
6178                        !!!parse-error (type => 'unmatched end tag',
6179                                        text => 'tr', token => $token);
6180                      ## Ignore the token                      ## Ignore the token
6181                        !!!nack ('t252.1');
6182                      !!!next-token;                      !!!next-token;
6183                      redo B;                      next B;
6184                    }                    }
6185                                    
6186                  ## Clear back to table row context                  ## Clear back to table row context
6187                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6188                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6189                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
6190                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6191                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6192                  }                  }
6193                                    
# Line 3985  sub _tree_construction_main ($) { Line 6200  sub _tree_construction_main ($) {
6200                my $i;                my $i;
6201                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6202                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6203                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6204                      !!!cp ('t254');
6205                    $i = $_;                    $i = $_;
6206                    last INSCOPE;                    last INSCOPE;
6207                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6208                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6209                    last INSCOPE;                    last INSCOPE;
6210                  }                  }
6211                } # INSCOPE                } # INSCOPE
6212                unless (defined $i) {                unless (defined $i) {
6213                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
6214                    !!!parse-error (type => 'unmatched end tag',
6215                                    text => $token->{tag_name}, token => $token);
6216                  ## Ignore the token                  ## Ignore the token
6217                    !!!nack ('t256.1');
6218                  !!!next-token;                  !!!next-token;
6219                  redo B;                  next B;
6220                }                }
6221    
6222                ## Clear back to table body context                ## Clear back to table body context
6223                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6224                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6225                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6226                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6227                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6228                }                }
6229    
6230                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6231                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6232                  !!!nack ('t257.1');
6233                !!!next-token;                !!!next-token;
6234                redo B;                next B;
6235              } elsif ({              } elsif ({
6236                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6237                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6238                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6239                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6240                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6241                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6242                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6243                !!!next-token;                            text => $token->{tag_name}, token => $token);
6244                redo B;            ## Ignore the token
6245          } else {            !!!nack ('t258.1');
6246            !!!parse-error (type => 'in table:/'.$token->{tag_name});             !!!next-token;
6247              next B;
6248            } else {
6249              !!!cp ('t259');
6250              !!!parse-error (type => 'in table:/',
6251                              text => $token->{tag_name}, token => $token);
6252    
6253            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6254            #            #
6255          }          }
6256          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6257            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6258                    @{$self->{open_elements}} == 1) { # redundant, maybe
6259              !!!parse-error (type => 'in body:#eof', token => $token);
6260              !!!cp ('t259.1');
6261              #
6262            } else {
6263              !!!cp ('t259.2');
6264              #
6265            }
6266    
6267            ## Stop parsing
6268            last B;
6269        } else {        } else {
6270          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6271        }        }
6272      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6273            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6274              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6275                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6276                unless (length $token->{data}) {                unless (length $token->{data}) {
6277                    !!!cp ('t260');
6278                  !!!next-token;                  !!!next-token;
6279                  redo B;                  next B;
6280                }                }
6281              }              }
6282                            
6283                !!!cp ('t261');
6284              #              #
6285            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6286              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6287                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
6288                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6289                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6290                  !!!ack ('t262.1');
6291                !!!next-token;                !!!next-token;
6292                redo B;                next B;
6293              } else {              } else {
6294                  !!!cp ('t263');
6295                #                #
6296              }              }
6297            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6298              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6299                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6300                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
6301                    !!!parse-error (type => 'unmatched end tag',
6302                                    text => 'colgroup', token => $token);
6303                  ## Ignore the token                  ## Ignore the token
6304                  !!!next-token;                  !!!next-token;
6305                  redo B;                  next B;
6306                } else {                } else {
6307                    !!!cp ('t265');
6308                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6309                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6310                  !!!next-token;                  !!!next-token;
6311                  redo B;                              next B;            
6312                }                }
6313              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6314                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
6315                  !!!parse-error (type => 'unmatched end tag',
6316                                  text => 'col', token => $token);
6317                ## Ignore the token                ## Ignore the token
6318                !!!next-token;                !!!next-token;
6319                redo B;                next B;
6320              } else {              } else {
6321                  !!!cp ('t267');
6322                #                #
6323              }              }
6324            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6325              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6326            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6327              !!!cp ('t270.2');
6328              ## Stop parsing.
6329              last B;
6330            } else {
6331              ## NOTE: As if </colgroup>.
6332              !!!cp ('t270.1');
6333              pop @{$self->{open_elements}}; # colgroup
6334              $self->{insertion_mode} = IN_TABLE_IM;
6335              ## Reprocess.
6336              next B;
6337            }
6338          } else {
6339            die "$0: $token->{type}: Unknown token type";
6340          }
6341    
6342            ## As if </colgroup>            ## As if </colgroup>
6343            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6344              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
6345    ## TODO: Wrong error type?
6346                !!!parse-error (type => 'unmatched end tag',
6347                                text => 'colgroup', token => $token);
6348              ## Ignore the token              ## Ignore the token
6349                !!!nack ('t269.1');
6350              !!!next-token;              !!!next-token;
6351              redo B;              next B;
6352            } else {            } else {
6353                !!!cp ('t270');
6354              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6355              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6356                !!!ack-later;
6357              ## reprocess              ## reprocess
6358              redo B;              next B;
6359            }            }
6360      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6361        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6362            !!!cp ('t271');
6363          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6364          !!!next-token;          !!!next-token;
6365          redo B;          next B;
6366        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6367              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6368                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6369                  ## As if </option>              !!!cp ('t272');
6370                  pop @{$self->{open_elements}};              ## As if </option>
6371                }              pop @{$self->{open_elements}};
6372              } else {
6373                !!!cp ('t273');
6374              }
6375    
6376                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6377                !!!next-token;            !!!nack ('t273.1');
6378                redo B;            !!!next-token;
6379              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6380                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6381                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6382                  pop @{$self->{open_elements}};              !!!cp ('t274');
6383                }              ## As if </option>
6384                pop @{$self->{open_elements}};
6385              } else {
6386                !!!cp ('t275');
6387              }
6388    
6389                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6390                  ## As if </optgroup>              !!!cp ('t276');
6391                  pop @{$self->{open_elements}};              ## As if </optgroup>
6392                }              pop @{$self->{open_elements}};
6393              } else {
6394                !!!cp ('t277');
6395              }
6396    
6397                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6398                !!!next-token;            !!!nack ('t277.1');
6399                redo B;            !!!next-token;
6400              } elsif ($token->{tag_name} eq 'select') {            next B;
6401                !!!parse-error (type => 'not closed:select');          } elsif ({
6402                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6403                ## have an element in table scope                   }->{$token->{tag_name}} or
6404                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6405                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6406                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6407                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6408                    $i = $_;                     tr => 1, td => 1, th => 1,
6409                    last INSCOPE;                    }->{$token->{tag_name}})) {
6410                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6411                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6412                           }->{$node->[1]}) {                            token => $token);
6413                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6414                  }            ## as if there were </select> (otherwise).
6415                } # INSCOPE            ## have an element in table scope
6416                unless (defined $i) {            my $i;
6417                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6418                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6419                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6420                  redo B;                !!!cp ('t278');
6421                }                $i = $_;
6422                  last INSCOPE;
6423                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6424                  !!!cp ('t279');
6425                  last INSCOPE;
6426                }
6427              } # INSCOPE
6428              unless (defined $i) {
6429                !!!cp ('t280');
6430                !!!parse-error (type => 'unmatched end tag',
6431                                text => 'select', token => $token);
6432                ## Ignore the token
6433                !!!nack ('t280.1');
6434                !!!next-token;
6435                next B;
6436              }
6437                                
6438                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6439              splice @{$self->{open_elements}}, $i;
6440    
6441                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6442    
6443                !!!next-token;            if ($token->{tag_name} eq 'select') {
6444                redo B;              !!!nack ('t281.2');
6445                !!!next-token;
6446                next B;
6447              } else {
6448                !!!cp ('t281.1');
6449                !!!ack-later;
6450                ## Reprocess the token.
6451                next B;
6452              }
6453          } else {          } else {
6454            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!cp ('t282');
6455              !!!parse-error (type => 'in select',
6456                              text => $token->{tag_name}, token => $token);
6457            ## Ignore the token            ## Ignore the token
6458              !!!nack ('t282.1');
6459            !!!next-token;            !!!next-token;
6460            redo B;            next B;
6461          }          }
6462        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6463              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6464                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6465                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6466                  ## As if </option>              !!!cp ('t283');
6467                  splice @{$self->{open_elements}}, -2;              ## As if </option>
6468                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              splice @{$self->{open_elements}}, -2;
6469                  pop @{$self->{open_elements}};            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6470                } else {              !!!cp ('t284');
6471                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              pop @{$self->{open_elements}};
6472                  ## Ignore the token            } else {
6473                }              !!!cp ('t285');
6474                !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6475                redo B;                              text => $token->{tag_name}, token => $token);
6476              } elsif ($token->{tag_name} eq 'option') {              ## Ignore the token
6477                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6478                  pop @{$self->{open_elements}};            !!!nack ('t285.1');
6479                } else {            !!!next-token;
6480                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            next B;
6481                  ## Ignore the token          } elsif ($token->{tag_name} eq 'option') {
6482                }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6483                !!!next-token;              !!!cp ('t286');
6484                redo B;              pop @{$self->{open_elements}};
6485              } elsif ($token->{tag_name} eq 'select') {            } else {
6486                ## have an element in table scope              !!!cp ('t287');
6487                my $i;              !!!parse-error (type => 'unmatched end tag',
6488                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                              text => $token->{tag_name}, token => $token);
6489                  my $node = $self->{open_elements}->[$_];              ## Ignore the token
6490                  if ($node->[1] eq $token->{tag_name}) {            }
6491                    $i = $_;            !!!nack ('t287.1');
6492                    last INSCOPE;            !!!next-token;
6493                  } elsif ({            next B;
6494                            table => 1, html => 1,          } elsif ($token->{tag_name} eq 'select') {
6495                           }->{$node->[1]}) {            ## have an element in table scope
6496                    last INSCOPE;            my $i;
6497                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6498                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6499                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t288');
6501                  ## Ignore the token                $i = $_;
6502                  !!!next-token;                last INSCOPE;
6503                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6504                }                !!!cp ('t289');
6505                  last INSCOPE;
6506                }
6507              } # INSCOPE
6508              unless (defined $i) {
6509                !!!cp ('t290');
6510                !!!parse-error (type => 'unmatched end tag',
6511                                text => $token->{tag_name}, token => $token);
6512                ## Ignore the token
6513                !!!nack ('t290.1');
6514                !!!next-token;
6515                next B;
6516              }
6517                                
6518                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6519              splice @{$self->{open_elements}}, $i;
6520    
6521                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6522    
6523                !!!next-token;            !!!nack ('t291.1');
6524                redo B;            !!!next-token;
6525              } elsif ({            next B;
6526                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6527                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6528                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6529                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6530                     }->{$token->{tag_name}}) {
6531    ## TODO: The following is wrong?
6532              !!!parse-error (type => 'unmatched end tag',
6533                              text => $token->{tag_name}, token => $token);
6534                                
6535                ## have an element in table scope            ## have an element in table scope
6536                my $i;            my $i;
6537                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6538                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6539                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6540                    $i = $_;                !!!cp ('t292');
6541                    last INSCOPE;                $i = $_;
6542                  } elsif ({                last INSCOPE;
6543                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6544                           }->{$node->[1]}) {                !!!cp ('t293');
6545                    last INSCOPE;                last INSCOPE;
6546                  }              }
6547                } # INSCOPE            } # INSCOPE
6548                unless (defined $i) {            unless (defined $i) {
6549                  ## Ignore the token              !!!cp ('t294');
6550                  !!!next-token;              ## Ignore the token
6551                  redo B;              !!!nack ('t294.1');
6552                }              !!!next-token;
6553                next B;
6554              }
6555                                
6556                ## As if </select>            ## As if </select>
6557                ## have an element in table scope            ## have an element in table scope
6558                undef $i;            undef $i;
6559                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6560                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6561                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6562                    $i = $_;                !!!cp ('t295');
6563                    last INSCOPE;                $i = $_;
6564                  } elsif ({                last INSCOPE;
6565                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6566                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
6567                    last INSCOPE;                !!!cp ('t296');
6568                  }                last INSCOPE;
6569                } # INSCOPE              }
6570                unless (defined $i) {            } # INSCOPE
6571                  !!!parse-error (type => 'unmatched end tag:select');            unless (defined $i) {
6572                  ## Ignore the </select> token              !!!cp ('t297');
6573                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
6574                  redo B;              !!!parse-error (type => 'unmatched end tag',
6575                }                              text => 'select', token => $token);
6576                ## Ignore the </select> token
6577                !!!nack ('t297.1');
6578                !!!next-token; ## TODO: ok?
6579                next B;
6580              }
6581                                
6582                splice @{$self->{open_elements}}, $i;            !!!cp ('t298');
6583              splice @{$self->{open_elements}}, $i;
6584    
6585                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6586    
6587                ## reprocess            !!!ack-later;
6588                redo B;            ## reprocess
6589              next B;
6590          } else {          } else {
6591            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!cp ('t299');
6592              !!!parse-error (type => 'in select:/',
6593                              text => $token->{tag_name}, token => $token);
6594            ## Ignore the token            ## Ignore the token
6595              !!!nack ('t299.3');
6596            !!!next-token;            !!!next-token;
6597            redo B;            next B;
6598            }
6599          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6600            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6601                    @{$self->{open_elements}} == 1) { # redundant, maybe
6602              !!!cp ('t299.1');
6603              !!!parse-error (type => 'in body:#eof', token => $token);
6604            } else {
6605              !!!cp ('t299.2');
6606          }          }
6607    
6608            ## Stop parsing.
6609            last B;
6610        } else {        } else {
6611          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6612        }        }
6613      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6614        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6615          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6616            my $data = $1;            my $data = $1;
6617            ## As if in body            ## As if in body
6618            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 4274  sub _tree_construction_main ($) { Line 6620  sub _tree_construction_main ($) {
6620            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6621                        
6622            unless (length $token->{data}) {            unless (length $token->{data}) {
6623                !!!cp ('t300');
6624              !!!next-token;              !!!next-token;
6625              redo B;              next B;
6626            }            }
6627          }          }
6628                    
6629          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6630            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
6631              !!!parse-error (type => 'after html:#text', token => $token);
6632            ## Reprocess in the "main" phase, "after body" insertion mode...            #
6633            } else {
6634              !!!cp ('t302');
6635              ## "after body" insertion mode
6636              !!!parse-error (type => 'after body:#text', token => $token);
6637              #
6638          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6639    
6640          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6641          ## reprocess          ## reprocess
6642          redo B;          next B;
6643        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6644          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6645            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
6646                        !!!parse-error (type => 'after html',
6647            ## Reprocess in the "main" phase, "after body" insertion mode...                            text => $token->{tag_name}, token => $token);
6648              #
6649            } else {
6650              !!!cp ('t304');
6651              ## "after body" insertion mode
6652              !!!parse-error (type => 'after body',
6653                              text => $token->{tag_name}, token => $token);
6654              #
6655          }          }
6656    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6657          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6658            !!!ack-later;
6659          ## reprocess          ## reprocess
6660          redo B;          next B;
6661        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6662          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6663            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
6664              !!!parse-error (type => 'after html:/',
6665                              text => $token->{tag_name}, token => $token);
6666                        
6667            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6668            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess.
6669              next B;
6670            } else {
6671              !!!cp ('t306');
6672          }          }
6673    
6674          ## "after body" insertion mode          ## "after body" insertion mode
6675          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6676            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6677              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
6678                !!!parse-error (type => 'unmatched end tag',
6679                                text => 'html', token => $token);
6680              ## Ignore the token              ## Ignore the token
6681              !!!next-token;              !!!next-token;
6682              redo B;              next B;
6683            } else {            } else {
6684                !!!cp ('t308');
6685              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6686              !!!next-token;              !!!next-token;
6687              redo B;              next B;
6688            }            }
6689          } else {          } else {
6690            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
6691              !!!parse-error (type => 'after body:/',
6692                              text => $token->{tag_name}, token => $token);
6693    
6694            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6695            ## reprocess            ## reprocess
6696            redo B;            next B;
6697          }          }
6698          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6699            !!!cp ('t309.2');
6700            ## Stop parsing
6701            last B;
6702        } else {        } else {
6703          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6704        }        }
6705      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6706        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6707          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6708            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6709                        
6710            unless (length $token->{data}) {            unless (length $token->{data}) {
6711                !!!cp ('t310');
6712              !!!next-token;              !!!next-token;
6713              redo B;              next B;
6714            }            }
6715          }          }
6716                    
6717          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6718            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6719              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
6720                !!!parse-error (type => 'in frameset:#text', token => $token);
6721            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6722              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
6723            } else { # "after html frameset"              !!!parse-error (type => 'after frameset:#text', token => $token);
6724              !!!parse-error (type => 'after html:#character');            } else { # "after after frameset"
6725                !!!cp ('t313');
6726              $self->{insertion_mode} = AFTER_FRAMESET_IM;              !!!parse-error (type => 'after html:#text', token => $token);
             ## Reprocess in the "main" phase, "after frameset"...  
             !!!parse-error (type => 'after frameset:#character');  
6727            }            }
6728                        
6729            ## Ignore the token.            ## Ignore the token.
6730            if (length $token->{data}) {            if (length $token->{data}) {
6731                !!!cp ('t314');
6732              ## reprocess the rest of characters              ## reprocess the rest of characters
6733            } else {            } else {
6734                !!!cp ('t315');
6735              !!!next-token;              !!!next-token;
6736            }            }
6737            redo B;            next B;
6738          }          }
6739                    
6740          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6741        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6742          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6743              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6744            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6745              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6746              !!!nack ('t318.1');
6747            !!!next-token;            !!!next-token;
6748            redo B;            next B;
6749          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6750                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6751            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
6752              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6753            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6754              !!!ack ('t319.1');
6755            !!!next-token;            !!!next-token;
6756            redo B;            next B;
6757          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6758            ## NOTE: As if in body.            !!!cp ('t320');
6759            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6760            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6761              next B;
6762    
6763              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6764              ## has no parse error.
6765          } else {          } else {
6766            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6767              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
6768            } else {              !!!parse-error (type => 'in frameset',
6769              !!!parse-error (type => 'after frameset:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6770              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6771                !!!cp ('t322');
6772                !!!parse-error (type => 'after frameset',
6773                                text => $token->{tag_name}, token => $token);
6774              } else { # "after after frameset"
6775                !!!cp ('t322.2');
6776                !!!parse-error (type => 'after after frameset',
6777                                text => $token->{tag_name}, token => $token);
6778            }            }
6779            ## Ignore the token            ## Ignore the token
6780              !!!nack ('t322.1');
6781            !!!next-token;            !!!next-token;
6782            redo B;            next B;
6783          }          }
6784        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:/'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6785          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6786              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6787            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6788                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6789              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6790                !!!parse-error (type => 'unmatched end tag',
6791                                text => $token->{tag_name}, token => $token);
6792              ## Ignore the token              ## Ignore the token
6793              !!!next-token;              !!!next-token;
6794            } else {            } else {
6795                !!!cp ('t326');
6796              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6797              !!!next-token;              !!!next-token;
6798            }            }
6799    
6800            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6801                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6802                !!!cp ('t327');
6803              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6804              } else {
6805                !!!cp ('t328');
6806            }            }
6807            redo B;            next B;
6808          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6809                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6810              !!!cp ('t329');
6811            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6812            !!!next-token;            !!!next-token;
6813            redo B;            next B;
6814          } else {          } else {
6815            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6816              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
6817            } else {              !!!parse-error (type => 'in frameset:/',
6818              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6819              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6820                !!!cp ('t330.1');
6821                !!!parse-error (type => 'after frameset:/',
6822                                text => $token->{tag_name}, token => $token);
6823              } else { # "after after html"
6824                !!!cp ('t331');
6825                !!!parse-error (type => 'after after frameset:/',
6826                                text => $token->{tag_name}, token => $token);
6827            }            }
6828            ## Ignore the token            ## Ignore the token
6829            !!!next-token;            !!!next-token;
6830            redo B;            next B;
6831            }
6832          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6833            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6834                    @{$self->{open_elements}} == 1) { # redundant, maybe
6835              !!!cp ('t331.1');
6836              !!!parse-error (type => 'in body:#eof', token => $token);
6837            } else {
6838              !!!cp ('t331.2');
6839          }          }
6840            
6841            ## Stop parsing
6842            last B;
6843        } else {        } else {
6844          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6845        }        }
   
       ## ISSUE: An issue in spec here  
6846      } else {      } else {
6847        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6848      }      }
# Line 4453  sub _tree_construction_main ($) { Line 6850  sub _tree_construction_main ($) {
6850      ## "in body" insertion mode      ## "in body" insertion mode
6851      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
6852        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6853            !!!cp ('t332');
6854          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6855          $script_start_tag->($insert);          $script_start_tag->();
6856          redo B;          next B;
6857        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6858            !!!cp ('t333');
6859          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6860          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6861          redo B;          next B;
6862        } elsif ({        } elsif ({
6863                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6864                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6865            !!!cp ('t334');
6866          ## 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
6867          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6868          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6869            !!!ack ('t334.1');
6870          !!!next-token;          !!!next-token;
6871          redo B;          next B;
6872        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6873          ## 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
6874          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6875          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6876    
6877          unless ($self->{confident}) {          unless ($self->{confident}) {
6878            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6879                !!!cp ('t335');
6880                ## NOTE: Whether the encoding is supported or not is handled
6881                ## in the {change_encoding} callback.
6882              $self->{change_encoding}              $self->{change_encoding}
6883                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6884                            
6885              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6886                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6887                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6888                                           ->{has_reference});                                           ->{has_reference});
6889            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6890              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6891                  =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6892                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*=
6893                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6894                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6895                       /x) {
6896                  !!!cp ('t336');
6897                  ## NOTE: Whether the encoding is supported or not is handled
6898                  ## in the {change_encoding} callback.
6899                $self->{change_encoding}                $self->{change_encoding}
6900                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6901                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6902                      ->set_user_data (manakai_has_reference =>
6903                                           $token->{attributes}->{content}
6904                                                 ->{has_reference});
6905              }              }
6906            }            }
6907          } else {          } else {
6908            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
6909                !!!cp ('t337');
6910              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6911                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6912                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6913                                           ->{has_reference});                                           ->{has_reference});
6914            }            }
6915              if ($token->{attributes}->{content}) {
6916                !!!cp ('t338');
6917                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6918                    ->set_user_data (manakai_has_reference =>
6919                                         $token->{attributes}->{content}
6920                                             ->{has_reference});
6921              }
6922          }          }
6923    
6924            !!!ack ('t338.1');
6925          !!!next-token;          !!!next-token;
6926          redo B;          next B;
6927        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6928          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
6929          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6930          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6931            if (defined $self->{head_element}) {          next B;
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6932        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6933          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6934                                
6935          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6936              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6937              !!!cp ('t342');
6938            ## Ignore the token            ## Ignore the token
6939          } else {          } else {
6940            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
6941            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
6942              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6943                  !!!cp ('t343');
6944                $body_el->set_attribute_ns                $body_el->set_attribute_ns
6945                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
6946                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
6947              }              }
6948            }            }
6949          }          }
6950            !!!nack ('t343.1');
6951          !!!next-token;          !!!next-token;
6952          redo B;          next B;
6953        } elsif ({        } elsif ({
6954                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6955                  div => 1, dl => 1, fieldset => 1, listing => 1,  
6956                  menu => 1, ol => 1, p => 1, ul => 1,                  ## NOTE: The normal one
6957                  pre => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6958                    center => 1, datagrid => 1, details => 1, dialog => 1,
6959                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6960                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6961                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6962                    section => 1, ul => 1,
6963                    ## NOTE: As normal, but drops leading newline
6964                    pre => 1, listing => 1,
6965                    ## NOTE: As normal, but interacts with the form element pointer
6966                    form => 1,
6967                    
6968                    table => 1,
6969                    hr => 1,
6970                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6971            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6972              !!!cp ('t350');
6973              !!!parse-error (type => 'in form:form', token => $token);
6974              ## Ignore the token
6975              !!!nack ('t350.1');
6976              !!!next-token;
6977              next B;
6978            }
6979    
6980          ## has a p element in scope          ## has a p element in scope
6981          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
6982            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
6983              !!!back-token;              !!!cp ('t344');
6984              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <form>
6985              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
6986            } elsif ({                        line => $token->{line}, column => $token->{column}};
6987                      table => 1, caption => 1, td => 1, th => 1,              next B;
6988                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
6989                     }->{$_->[1]}) {              !!!cp ('t345');
6990              last INSCOPE;              last INSCOPE;
6991            }            }
6992          } # INSCOPE          } # INSCOPE
6993                        
6994          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6995          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6996              !!!nack ('t346.1');
6997            !!!next-token;            !!!next-token;
6998            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6999              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
7000              unless (length $token->{data}) {              unless (length $token->{data}) {
7001                  !!!cp ('t346');
7002                !!!next-token;                !!!next-token;
7003                } else {
7004                  !!!cp ('t349');
7005              }              }
7006              } else {
7007                !!!cp ('t348');
7008            }            }
7009          } else {          } elsif ($token->{tag_name} eq 'form') {
7010              !!!cp ('t347.1');
7011              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7012    
7013              !!!nack ('t347.2');
7014            !!!next-token;            !!!next-token;
7015          }          } elsif ($token->{tag_name} eq 'table') {
7016          redo B;            !!!cp ('t382');
7017        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7018          if (defined $self->{form_element}) {            
7019            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
7020            ## Ignore the token  
7021              !!!nack ('t382.1');
7022              !!!next-token;
7023            } elsif ($token->{tag_name} eq 'hr') {
7024              !!!cp ('t386');
7025              pop @{$self->{open_elements}};
7026            
7027              !!!nack ('t386.1');
7028            !!!next-token;            !!!next-token;
           redo B;  
7029          } else {          } else {
7030            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!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]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
7031            !!!next-token;            !!!next-token;
           redo B;  
7032          }          }
7033            next B;
7034        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7035          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7036          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7037            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7038              !!!back-token;            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7039              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7040              redo B;            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7041            } elsif ({            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7042                      table => 1, caption => 1, td => 1, th => 1,            ## object (Fx)
7043                      button => 1, marquee => 1, object => 1, html => 1,            ## Generate non-tree (non-conforming)
7044                     }->{$_->[1]}) {            ## basefont (IE7 (where basefont is non-void)), center (IE),
7045              last INSCOPE;            ## form (IE), hn (IE)
7046            }          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7047          } # INSCOPE            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7048                        ## div (Fx, S)
7049          ## Step 1  
7050            my $non_optional;
7051          my $i = -1;          my $i = -1;
7052          my $node = $self->{open_elements}->[$i];  
7053          LI: {          ## 1.
7054            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7055            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7056              if ($i != -1) {              ## 2. (a) As if </li>
7057                !!!parse-error (type => 'end tag missing:'.              {
7058                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7059                  #
7060    
7061                  ## Otherwise
7062    
7063                  ## 1. generate implied end tags, except for </li>
7064                  #
7065    
7066                  ## 2. If current node != "li", parse error
7067                  if ($non_optional) {
7068                    !!!parse-error (type => 'not closed',
7069                                    text => $non_optional->[0]->manakai_local_name,
7070                                    token => $token);
7071                    !!!cp ('t355');
7072                  } else {
7073                    !!!cp ('t356');
7074                  }
7075    
7076                  ## 3. Pop
7077                  splice @{$self->{open_elements}}, $i;
7078              }              }
7079              splice @{$self->{open_elements}}, $i;  
7080              last LI;              last; ## 2. (b) goto 5.
7081            }            } elsif (
7082                                 ## NOTE: not "formatting" and not "phrasing"
7083            ## Step 3                     ($node->[1] & SPECIAL_EL or
7084            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7085                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7086                ($special_category->{$node->[1]} or  
7087                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7088                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7089              last LI;                     (not $node->[1] & P_EL)) {
7090                ## 3.
7091                !!!cp ('t357');
7092                last; ## goto 5.
7093              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7094                !!!cp ('t358');
7095                #
7096              } else {
7097                !!!cp ('t359');
7098                $non_optional ||= $node;
7099                #
7100            }            }
7101                        ## 4.
7102            ## Step 4            ## goto 2.
7103            $i--;            $i--;
7104            $node = $self->{open_elements}->[$i];          }
7105            redo LI;  
7106          } # 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  
7107          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7108            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7109              !!!back-token;              !!!cp ('t353');
7110              $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
7111              redo B;              ## NOTE: |<p><li>|, for example.
7112            } elsif ({  
7113                      table => 1, caption => 1, td => 1, th => 1,              !!!back-token; # <x>
7114                      button => 1, marquee => 1, object => 1, html => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7115                     }->{$_->[1]}) {                        line => $token->{line}, column => $token->{column}};
7116                next B;
7117              } elsif ($_->[1] & SCOPING_EL) {
7118                !!!cp ('t354');
7119              last INSCOPE;              last INSCOPE;
7120            }            }
7121          } # INSCOPE          } # INSCOPE
7122              
7123          ## Step 1          ## 5. (b) insert
7124            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7125            !!!nack ('t359.1');
7126            !!!next-token;
7127            next B;
7128          } elsif ($token->{tag_name} eq 'dt' or
7129                   $token->{tag_name} eq 'dd') {
7130            ## NOTE: As normal, but imply </dt> or </dd> when ...
7131    
7132            my $non_optional;
7133          my $i = -1;          my $i = -1;
7134          my $node = $self->{open_elements}->[$i];  
7135          LI: {          ## 1.
7136            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7137            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7138              if ($i != -1) {              ## 2. (a) As if </li>
7139                !!!parse-error (type => 'end tag missing:'.              {
7140                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7141                  #
7142    
7143                  ## Otherwise
7144    
7145                  ## 1. generate implied end tags, except for </dt> or </dd>
7146                  #
7147    
7148                  ## 2. If current node != "dt"|"dd", parse error
7149                  if ($non_optional) {
7150                    !!!parse-error (type => 'not closed',
7151                                    text => $non_optional->[0]->manakai_local_name,
7152                                    token => $token);
7153                    !!!cp ('t355.1');
7154                  } else {
7155                    !!!cp ('t356.1');
7156                  }
7157    
7158                  ## 3. Pop
7159                  splice @{$self->{open_elements}}, $i;
7160              }              }
7161              splice @{$self->{open_elements}}, $i;  
7162              last LI;              last; ## 2. (b) goto 5.
7163            }            } elsif (
7164                                 ## NOTE: not "formatting" and not "phrasing"
7165            ## Step 3                     ($node->[1] & SPECIAL_EL or
7166            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7167                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7168                ($special_category->{$node->[1]} or  
7169                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7170                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7171              last LI;                     (not $node->[1] & P_EL)) {
7172                ## 3.
7173                !!!cp ('t357.1');
7174                last; ## goto 5.
7175              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7176                !!!cp ('t358.1');
7177                #
7178              } else {
7179                !!!cp ('t359.1');
7180                $non_optional ||= $node;
7181                #
7182            }            }
7183                        ## 4.
7184            ## Step 4            ## goto 2.
7185            $i--;            $i--;
7186            $node = $self->{open_elements}->[$i];          }
7187            redo LI;  
7188          } # 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 'plaintext') {  
         ## has a p element in scope  
7189          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7190            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7191              !!!back-token;              !!!cp ('t353.1');
7192              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <x>
7193              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7194            } elsif ({                        line => $token->{line}, column => $token->{column}};
7195                      table => 1, caption => 1, td => 1, th => 1,              next B;
7196                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7197                     }->{$_->[1]}) {              !!!cp ('t354.1');
7198              last INSCOPE;              last INSCOPE;
7199            }            }
7200          } # INSCOPE          } # INSCOPE
7201              
7202          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          ## 5. (b) insert
7203                      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7204          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          !!!nack ('t359.2');
             
7205          !!!next-token;          !!!next-token;
7206          redo B;          next B;
7207        } elsif ({        } elsif ($token->{tag_name} eq 'plaintext') {
7208                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          ## NOTE: As normal, but effectively ends parsing
7209                 }->{$token->{tag_name}}) {  
7210          ## has a p element in scope          ## has a p element in scope
7211          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7212            my $node = $self->{open_elements}->[$_];            if ($_->[1] & P_EL) {
7213            if ($node->[1] eq 'p') {              !!!cp ('t367');
7214              !!!back-token;              !!!back-token; # <plaintext>
7215              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7216              redo B;                        line => $token->{line}, column => $token->{column}};
7217            } elsif ({              next B;
7218                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
7219                      button => 1, marquee => 1, object => 1, html => 1,              !!!cp ('t368');
                    }->{$node->[1]}) {  
7220              last INSCOPE;              last INSCOPE;
7221            }            }
7222          } # INSCOPE          } # INSCOPE
7223                        
7224          ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
7225                        
7226          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7227                        
7228            !!!nack ('t368.1');
7229          !!!next-token;          !!!next-token;
7230          redo B;          next B;
7231        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7232          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7233            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7234            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7235              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
7236                !!!parse-error (type => 'in a:a', token => $token);
7237                            
7238              !!!back-token;              !!!back-token; # <a>
7239              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7240              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7241                $formatting_end_tag->($token);
7242                            
7243              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7244                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7245                    !!!cp ('t372');
7246                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
7247                  last AFE2;                  last AFE2;
7248                }                }
7249              } # AFE2              } # AFE2
7250              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
7251                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7252                    !!!cp ('t373');
7253                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
7254                  last OE;                  last OE;
7255                }                }
7256              } # OE              } # OE
7257              last AFE;              last AFE;
7258            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
7259                !!!cp ('t374');
7260              last AFE;              last AFE;
7261            }            }
7262          } # AFE          } # AFE
7263                        
7264          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7265    
7266          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7267          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7268    
7269            !!!nack ('t374.1');
7270          !!!next-token;          !!!next-token;
7271          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}}) {  
         $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;  
7272        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7273          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7274    
7275          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7276          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7277            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7278            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7279              !!!parse-error (type => 'in nobr:nobr');              !!!cp ('t376');
7280              !!!back-token;              !!!parse-error (type => 'in nobr:nobr', token => $token);
7281              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              !!!back-token; # <nobr>
7282              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7283            } elsif ({                        line => $token->{line}, column => $token->{column}};
7284                      table => 1, caption => 1, td => 1, th => 1,              next B;
7285                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7286                     }->{$node->[1]}) {              !!!cp ('t377');
7287              last INSCOPE;              last INSCOPE;
7288            }            }
7289          } # INSCOPE          } # INSCOPE
7290                    
7291          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7292          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7293                    
7294            !!!nack ('t377.1');
7295          !!!next-token;          !!!next-token;
7296          redo B;          next B;
7297        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7298          ## has a button element in scope          ## has a button element in scope
7299          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7300            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7301            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7302              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
7303              !!!back-token;              !!!parse-error (type => 'in button:button', token => $token);
7304              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              !!!back-token; # <button>
7305              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7306            } elsif ({                        line => $token->{line}, column => $token->{column}};
7307                      table => 1, caption => 1, td => 1, th => 1,              next B;
7308                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7309                     }->{$node->[1]}) {              !!!cp ('t379');
7310              last INSCOPE;              last INSCOPE;
7311            }            }
7312          } # INSCOPE          } # INSCOPE
7313                        
7314          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7315                        
7316          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7317          push @$active_formatting_elements, ['#marker', ''];  
7318            ## TODO: associate with $self->{form_element} if defined
7319    
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
7320          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7321            
7322          !!!next-token;          !!!nack ('t379.1');
         redo B;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         redo B;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!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]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = IN_TABLE_IM;  
             
7323          !!!next-token;          !!!next-token;
7324          redo B;          next B;
7325        } elsif ({        } elsif ({
7326                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7327                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7328                  image => 1,                  noembed => 1,
7329                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7330                    noscript => 0, ## TODO: 1 if scripting is enabled
7331                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7332          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7333            !!!parse-error (type => 'image');            !!!cp ('t381');
7334            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
7335            } else {
7336              !!!cp ('t399');
7337          }          }
7338            ## NOTE: There is an "as if in body" code clone.
7339          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7340          $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') {  
             !!!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]}) {  
             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') {  
         $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;  
7341        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7342          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7343                    
7344          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7345              !!!cp ('t389');
7346            ## Ignore the token            ## Ignore the token
7347              !!!nack ('t389'); ## NOTE: Not acknowledged.
7348            !!!next-token;            !!!next-token;
7349            redo B;            next B;
7350          } else {          } else {
7351              !!!ack ('t391.1');
7352    
7353            my $at = $token->{attributes};            my $at = $token->{attributes};
7354            my $form_attrs;            my $form_attrs;
7355            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 4944  sub _tree_construction_main ($) { Line 7359  sub _tree_construction_main ($) {
7359            delete $at->{prompt};            delete $at->{prompt};
7360            my @tokens = (            my @tokens = (
7361                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7362                           attributes => $form_attrs},                           attributes => $form_attrs,
7363                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7364                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7365                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7366                            {type => START_TAG_TOKEN, tag_name => 'p',
7367                             line => $token->{line}, column => $token->{column}},
7368                            {type => START_TAG_TOKEN, tag_name => 'label',
7369                             line => $token->{line}, column => $token->{column}},
7370                         );                         );
7371            if ($prompt_attr) {            if ($prompt_attr) {
7372              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
7373                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7374                               #line => $token->{line}, column => $token->{column},
7375                              };
7376            } else {            } else {
7377                !!!cp ('t391');
7378              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7379                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7380                               #line => $token->{line}, column => $token->{column},
7381                              }; # SHOULD
7382              ## TODO: make this configurable              ## TODO: make this configurable
7383            }            }
7384            push @tokens,            push @tokens,
7385                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7386                             line => $token->{line}, column => $token->{column}},
7387                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7388                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7389                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7390                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7391                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7392            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7393                             line => $token->{line}, column => $token->{column}},
7394                            {type => END_TAG_TOKEN, tag_name => 'form',
7395                             line => $token->{line}, column => $token->{column}};
7396            !!!back-token (@tokens);            !!!back-token (@tokens);
7397            redo B;            !!!next-token;
7398              next B;
7399          }          }
7400        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7401          my $tag_name = $token->{tag_name};          my $tag_name = $token->{tag_name};
7402          my $el;          my $el;
7403          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7404                    
7405          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7406          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
# Line 4979  sub _tree_construction_main ($) { Line 7409  sub _tree_construction_main ($) {
7409          $insert->($el);          $insert->($el);
7410                    
7411          my $text = '';          my $text = '';
7412            !!!nack ('t392.1');
7413          !!!next-token;          !!!next-token;
7414          if ($token->{type} == CHARACTER_TOKEN) {          if ($token->{type} == CHARACTER_TOKEN) {
7415            $token->{data} =~ s/^\x0A//;            $token->{data} =~ s/^\x0A//;
7416            unless (length $token->{data}) {            unless (length $token->{data}) {
7417                !!!cp ('t392');
7418              !!!next-token;              !!!next-token;
7419              } else {
7420                !!!cp ('t393');
7421            }            }
7422            } else {
7423              !!!cp ('t394');
7424          }          }
7425          while ($token->{type} == CHARACTER_TOKEN) {          while ($token->{type} == CHARACTER_TOKEN) {
7426              !!!cp ('t395');
7427            $text .= $token->{data};            $text .= $token->{data};
7428            !!!next-token;            !!!next-token;
7429          }          }
7430          if (length $text) {          if (length $text) {
7431              !!!cp ('t396');
7432            $el->manakai_append_text ($text);            $el->manakai_append_text ($text);
7433          }          }
7434                    
# Line 4998  sub _tree_construction_main ($) { Line 7436  sub _tree_construction_main ($) {
7436                    
7437          if ($token->{type} == END_TAG_TOKEN and          if ($token->{type} == END_TAG_TOKEN and
7438              $token->{tag_name} eq $tag_name) {              $token->{tag_name} eq $tag_name) {
7439              !!!cp ('t397');
7440            ## Ignore the token            ## Ignore the token
7441          } else {          } else {
7442            !!!parse-error (type => 'in RCDATA:#'.$token->{type});            !!!cp ('t398');
7443              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7444          }          }
7445          !!!next-token;          !!!next-token;
7446            next B;
7447          } elsif ($token->{tag_name} eq 'optgroup' or
7448                   $token->{tag_name} eq 'option') {
7449            ## has an |option| element in scope
7450            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7451              my $node = $self->{open_elements}->[$_];
7452              if ($node->[1] & OPTION_EL) {
7453                !!!cp ('t397.1');
7454                ## NOTE: As if </option>
7455                !!!back-token; # <option> or <optgroup>
7456                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7457                          line => $token->{line}, column => $token->{column}};
7458                next B;
7459              } elsif ($node->[1] & SCOPING_EL) {
7460                !!!cp ('t397.2');
7461                last INSCOPE;
7462              }
7463            } # INSCOPE
7464    
7465            $reconstruct_active_formatting_elements->($insert_to_current);
7466    
7467            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7468    
7469            !!!nack ('t397.3');
7470            !!!next-token;
7471          redo B;          redo B;
7472        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7473                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7474                  noembed => 1,          ## has a |ruby| element in scope
7475                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7476                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7477                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7478          ## NOTE: There is an "as if in body" code clone.              !!!cp ('t398.1');
7479          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              ## generate implied end tags
7480                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7481                  !!!cp ('t398.2');
7482                  pop @{$self->{open_elements}};
7483                }
7484                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7485                  !!!cp ('t398.3');
7486                  !!!parse-error (type => 'not closed',
7487                                  text => $self->{open_elements}->[-1]->[0]
7488                                      ->manakai_local_name,
7489                                  token => $token);
7490                  pop @{$self->{open_elements}}
7491                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7492                }
7493                last INSCOPE;
7494              } elsif ($node->[1] & SCOPING_EL) {
7495                !!!cp ('t398.4');
7496                last INSCOPE;
7497              }
7498            } # INSCOPE
7499    
7500            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7501    
7502            !!!nack ('t398.5');
7503            !!!next-token;
7504          redo B;          redo B;
7505        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7506                   $token->{tag_name} eq 'svg') {
7507          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7508    
7509            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7510    
7511            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7512    
7513            ## "adjust foreign attributes" - done in insert-element-f
7514                    
7515          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7516                    
7517          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7518              pop @{$self->{open_elements}};
7519              !!!ack ('t398.6');
7520            } else {
7521              !!!cp ('t398.7');
7522              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7523              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7524              ## mode, "in body" (not "in foreign content") secondary insertion
7525              ## mode, maybe.
7526            }
7527    
7528          !!!next-token;          !!!next-token;
7529          redo B;          next B;
7530        } elsif ({        } elsif ({
7531                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7532                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7533                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7534                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7535                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7536          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
7537            !!!parse-error (type => 'in body',
7538                            text => $token->{tag_name}, token => $token);
7539          ## Ignore the token          ## Ignore the token
7540            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7541            !!!next-token;
7542            next B;
7543          } elsif ($token->{tag_name} eq 'param' or
7544                   $token->{tag_name} eq 'source') {
7545            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7546            pop @{$self->{open_elements}};
7547    
7548            !!!ack ('t398.5');
7549          !!!next-token;          !!!next-token;
7550          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7551        } else {        } else {
7552            if ($token->{tag_name} eq 'image') {
7553              !!!cp ('t384');
7554              !!!parse-error (type => 'image', token => $token);
7555              $token->{tag_name} = 'img';
7556            } else {
7557              !!!cp ('t385');
7558            }
7559    
7560            ## NOTE: There is an "as if <br>" code clone.
7561          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7562                    
7563          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7564    
7565            if ({
7566                 applet => 1, marquee => 1, object => 1,
7567                }->{$token->{tag_name}}) {
7568              !!!cp ('t380');
7569              push @$active_formatting_elements, ['#marker', ''];
7570              !!!nack ('t380.1');
7571            } elsif ({
7572                      b => 1, big => 1, em => 1, font => 1, i => 1,
7573                      s => 1, small => 1, strike => 1,
7574                      strong => 1, tt => 1, u => 1,
7575                     }->{$token->{tag_name}}) {
7576              !!!cp ('t375');
7577              push @$active_formatting_elements, $self->{open_elements}->[-1];
7578              !!!nack ('t375.1');
7579            } elsif ($token->{tag_name} eq 'input') {
7580              !!!cp ('t388');
7581              ## TODO: associate with $self->{form_element} if defined
7582              pop @{$self->{open_elements}};
7583              !!!ack ('t388.2');
7584            } elsif ({
7585                      area => 1, basefont => 1, bgsound => 1, br => 1,
7586                      embed => 1, img => 1, spacer => 1, wbr => 1,
7587                     }->{$token->{tag_name}}) {
7588              !!!cp ('t388.1');
7589              pop @{$self->{open_elements}};
7590              !!!ack ('t388.3');
7591            } elsif ($token->{tag_name} eq 'select') {
7592              ## TODO: associate with $self->{form_element} if defined
7593            
7594              if ($self->{insertion_mode} & TABLE_IMS or
7595                  $self->{insertion_mode} & BODY_TABLE_IMS or
7596                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7597                !!!cp ('t400.1');
7598                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7599              } else {
7600                !!!cp ('t400.2');
7601                $self->{insertion_mode} = IN_SELECT_IM;
7602              }
7603              !!!nack ('t400.3');
7604            } else {
7605              !!!nack ('t402');
7606            }
7607                    
7608          !!!next-token;          !!!next-token;
7609          redo B;          next B;
7610        }        }
7611      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7612        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7613          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7614              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7615            for (@{$self->{open_elements}}) {          INSCOPE: {
7616              unless ({            for (reverse @{$self->{open_elements}}) {
7617                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7618                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7619                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7620                      }->{$_->[1]}) {                last INSCOPE;
7621                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
7622                  !!!cp ('t405.1');
7623                  last;
7624              }              }
7625            }            }
7626    
7627            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7628            !!!next-token;  
7629            redo B;            !!!parse-error (type => 'unmatched end tag',
7630          } else {                            text => $token->{tag_name}, token => $token);
7631            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: Ignore the token.
           ## Ignore the token  
7632            !!!next-token;            !!!next-token;
7633            redo B;            next B;
7634            } # INSCOPE
7635    
7636            for (@{$self->{open_elements}}) {
7637              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7638                !!!cp ('t403');
7639                !!!parse-error (type => 'not closed',
7640                                text => $_->[0]->manakai_local_name,
7641                                token => $token);
7642                last;
7643              } else {
7644                !!!cp ('t404');
7645              }
7646          }          }
7647    
7648            $self->{insertion_mode} = AFTER_BODY_IM;
7649            !!!next-token;
7650            next B;
7651        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7652          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
7653            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7654            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7655              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              $self->{open_elements}->[1]->[1] & BODY_EL) {
7656              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7657                !!!cp ('t406');
7658                !!!parse-error (type => 'not closed',
7659                                text => $self->{open_elements}->[1]->[0]
7660                                    ->manakai_local_name,
7661                                token => $token);
7662              } else {
7663                !!!cp ('t407');
7664            }            }
7665            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7666            ## reprocess            ## reprocess
7667            redo B;            next B;
7668          } else {          } else {
7669            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
7670              !!!parse-error (type => 'unmatched end tag',
7671                              text => $token->{tag_name}, token => $token);
7672            ## Ignore the token            ## Ignore the token
7673            !!!next-token;            !!!next-token;
7674            redo B;            next B;
7675          }          }
7676        } elsif ({        } elsif ({
7677                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7678                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7679                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7680                  p => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
7681                    center => 1, datagrid => 1, details => 1, dialog => 1,
7682                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7683                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7684                    ol => 1, pre => 1, section => 1, ul => 1,
7685    
7686                    ## NOTE: As normal, but ... optional tags
7687                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7688                  button => 1, marquee => 1, object => 1,  
7689                    applet => 1, button => 1, marquee => 1, object => 1,
7690                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7691            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7692            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7693            ## </dd>" code.
7694    
7695          ## has an element in scope          ## has an element in scope
7696          my $i;          my $i;
7697          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7698            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7699            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7700              ## generate implied end tags              !!!cp ('t410');
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7701              $i = $_;              $i = $_;
7702              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7703            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7704                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7705              last INSCOPE;              last INSCOPE;
7706            }            }
7707          } # INSCOPE          } # INSCOPE
7708            
7709          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7710            if (defined $i) {            !!!cp ('t413');
7711              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag',
7712                              text => $token->{tag_name}, token => $token);
7713              ## NOTE: Ignore the token.
7714            } else {
7715              ## Step 1. generate implied end tags
7716              while ({
7717                      ## END_TAG_OPTIONAL_EL
7718                      dd => ($token->{tag_name} ne 'dd'),
7719                      dt => ($token->{tag_name} ne 'dt'),
7720                      li => ($token->{tag_name} ne 'li'),
7721                      option => 1,
7722                      optgroup => 1,
7723                      p => 1,
7724                      rt => 1,
7725                      rp => 1,
7726                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7727                !!!cp ('t409');
7728                pop @{$self->{open_elements}};
7729              }
7730    
7731              ## Step 2.
7732              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7733                      ne $token->{tag_name}) {
7734                !!!cp ('t412');
7735                !!!parse-error (type => 'not closed',
7736                                text => $self->{open_elements}->[-1]->[0]
7737                                    ->manakai_local_name,
7738                                token => $token);
7739            } else {            } else {
7740              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
7741            }            }
7742          }  
7743                      ## Step 3.
         if (defined $i) {  
7744            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7745          } elsif ($token->{tag_name} eq 'p') {  
7746            ## As if <p>, then reprocess the current token            ## Step 4.
7747            my $el;            $clear_up_to_marker->()
7748            !!!create-element ($el, 'p');                if {
7749            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
7750                  }->{$token->{tag_name}};
7751          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
7752          !!!next-token;          !!!next-token;
7753          redo B;          next B;
7754        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7755            ## NOTE: As normal, but interacts with the form element pointer
7756    
7757            undef $self->{form_element};
7758    
7759          ## has an element in scope          ## has an element in scope
7760            my $i;
7761          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7762            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7763            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7764              ## generate implied end tags              !!!cp ('t418');
7765              if ({              $i = $_;
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7766              last INSCOPE;              last INSCOPE;
7767            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7768                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7769              last INSCOPE;              last INSCOPE;
7770            }            }
7771          } # INSCOPE          } # INSCOPE
7772            
7773          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7774            pop @{$self->{open_elements}};            !!!cp ('t421');
7775          } else {            !!!parse-error (type => 'unmatched end tag',
7776            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
7777              ## NOTE: Ignore the token.
7778            } else {
7779              ## Step 1. generate implied end tags
7780              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7781                !!!cp ('t417');
7782                pop @{$self->{open_elements}};
7783              }
7784              
7785              ## Step 2.
7786              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7787                      ne $token->{tag_name}) {
7788                !!!cp ('t417.1');
7789                !!!parse-error (type => 'not closed',
7790                                text => $self->{open_elements}->[-1]->[0]
7791                                    ->manakai_local_name,
7792                                token => $token);
7793              } else {
7794                !!!cp ('t420');
7795              }  
7796              
7797              ## Step 3.
7798              splice @{$self->{open_elements}}, $i;
7799          }          }
7800    
         undef $self->{form_element};  
7801          !!!next-token;          !!!next-token;
7802          redo B;          next B;
7803        } elsif ({        } elsif ({
7804                    ## NOTE: As normal, except acts as a closer for any ...
7805                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7806                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7807          ## has an element in scope          ## has an element in scope
7808          my $i;          my $i;
7809          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7810            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7811            if ({            if ($node->[1] & HEADING_EL) {
7812                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,              !!!cp ('t423');
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => END_TAG_TOKEN,  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
             }  
7813              $i = $_;              $i = $_;
7814              last INSCOPE;              last INSCOPE;
7815            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7816                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7817              last INSCOPE;              last INSCOPE;
7818            }            }
7819          } # INSCOPE          } # INSCOPE
7820            
7821          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7822            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t425.1');
7823              !!!parse-error (type => 'unmatched end tag',
7824                              text => $token->{tag_name}, token => $token);
7825              ## NOTE: Ignore the token.
7826            } else {
7827              ## Step 1. generate implied end tags
7828              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7829                !!!cp ('t422');
7830                pop @{$self->{open_elements}};
7831              }
7832              
7833              ## Step 2.
7834              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7835                      ne $token->{tag_name}) {
7836                !!!cp ('t425');
7837                !!!parse-error (type => 'unmatched end tag',
7838                                text => $token->{tag_name}, token => $token);
7839              } else {
7840                !!!cp ('t426');
7841              }
7842    
7843              ## Step 3.
7844              splice @{$self->{open_elements}}, $i;
7845          }          }
7846                    
         splice @{$self->{open_elements}}, $i if defined $i;  
7847          !!!next-token;          !!!next-token;
7848          redo B;          next B;
7849          } elsif ($token->{tag_name} eq 'p') {
7850            ## NOTE: As normal, except </p> implies <p> and ...
7851    
7852            ## has an element in scope
7853            my $non_optional;
7854            my $i;
7855            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7856              my $node = $self->{open_elements}->[$_];
7857              if ($node->[1] & P_EL) {
7858                !!!cp ('t410.1');
7859                $i = $_;
7860                last INSCOPE;
7861              } elsif ($node->[1] & SCOPING_EL) {
7862                !!!cp ('t411.1');
7863                last INSCOPE;
7864              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7865                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7866                !!!cp ('t411.2');
7867                #
7868              } else {
7869                !!!cp ('t411.3');
7870                $non_optional ||= $node;
7871                #
7872              }
7873            } # INSCOPE
7874    
7875            if (defined $i) {
7876              ## 1. Generate implied end tags
7877              #
7878    
7879              ## 2. If current node != "p", parse error
7880              if ($non_optional) {
7881                !!!cp ('t412.1');
7882                !!!parse-error (type => 'not closed',
7883                                text => $non_optional->[0]->manakai_local_name,
7884                                token => $token);
7885              } else {
7886                !!!cp ('t414.1');
7887              }
7888    
7889              ## 3. Pop
7890              splice @{$self->{open_elements}}, $i;
7891            } else {
7892              !!!cp ('t413.1');
7893              !!!parse-error (type => 'unmatched end tag',
7894                              text => $token->{tag_name}, token => $token);
7895    
7896              !!!cp ('t415.1');
7897              ## As if <p>, then reprocess the current token
7898              my $el;
7899              !!!create-element ($el, $HTML_NS, 'p',, $token);
7900              $insert->($el);
7901              ## NOTE: Not inserted into |$self->{open_elements}|.
7902            }
7903    
7904            !!!next-token;
7905            next B;
7906        } elsif ({        } elsif ({
7907                  a => 1,                  a => 1,
7908                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7909                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7910                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7911                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7912          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
7913          redo B;          $formatting_end_tag->($token);
7914            next B;
7915        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7916          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
7917            !!!parse-error (type => 'unmatched end tag',
7918                            text => 'br', token => $token);
7919    
7920          ## As if <br>          ## As if <br>
7921          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7922                    
7923          my $el;          my $el;
7924          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7925          $insert->($el);          $insert->($el);
7926                    
7927          ## Ignore the token.          ## Ignore the token.
7928          !!!next-token;          !!!next-token;
7929          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}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7930        } else {        } else {
7931            if ($token->{tag_name} eq 'sarcasm') {
7932              sleep 0.001; # take a deep breath
7933            }
7934    
7935          ## Step 1          ## Step 1
7936          my $node_i = -1;          my $node_i = -1;
7937          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7938    
7939          ## Step 2          ## Step 2
7940          S2: {          S2: {
7941            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7942              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7943              if ($node_tag_name eq $token->{tag_name}) {
7944              ## Step 1              ## Step 1
7945              ## generate implied end tags              ## generate implied end tags
7946              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7947                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
7948                   td => 1, th => 1, tr => 1,                ## NOTE: |<ruby><rt></ruby>|.
7949                   tbody => 1, tfoot => 1, thead => 1,                ## ISSUE: <ruby><rt></rt> will also take this code path,
7950                  }->{$self->{open_elements}->[-1]->[1]}) {                ## which seems wrong.
7951                !!!back-token;                pop @{$self->{open_elements}};
7952                $token = {type => END_TAG_TOKEN,                $node_i++;
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
7953              }              }
7954                    
7955              ## Step 2              ## Step 2
7956              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7957                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7958                $current_tag_name =~ tr/A-Z/a-z/;
7959                if ($current_tag_name ne $token->{tag_name}) {
7960                  !!!cp ('t431');
7961                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7962                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7963                                  text => $self->{open_elements}->[-1]->[0]
7964                                      ->manakai_local_name,
7965                                  token => $token);
7966                } else {
7967                  !!!cp ('t432');
7968              }              }
7969                            
7970              ## Step 3              ## Step 3
7971              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7972    
7973              !!!next-token;              !!!next-token;
7974              last S2;              last S2;
7975            } else {            } else {
7976              ## Step 3              ## Step 3
7977              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7978                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7979                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7980                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7981                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
7982                  !!!parse-error (type => 'unmatched end tag',
7983                                  text => $token->{tag_name}, token => $token);
7984                ## Ignore the token                ## Ignore the token
7985                !!!next-token;                !!!next-token;
7986                last S2;                last S2;
7987    
7988                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7989                  ## 9.27, "a" is a child of <dd> (conforming).  In
7990                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7991                  ## "a" is a child of both <body> and <dd>.
7992              }              }
7993                
7994                !!!cp ('t434');
7995            }            }
7996                        
7997            ## Step 4            ## Step 4
# Line 5302  sub _tree_construction_main ($) { Line 8001  sub _tree_construction_main ($) {
8001            ## Step 5;            ## Step 5;
8002            redo S2;            redo S2;
8003          } # S2          } # S2
8004          redo B;          next B;
8005        }        }
8006      }      }
8007      redo B;      next B;
8008      } continue { # B
8009        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8010          ## NOTE: The code below is executed in cases where it does not have
8011          ## to be, but it it is harmless even in those cases.
8012          ## has an element in scope
8013          INSCOPE: {
8014            for (reverse 0..$#{$self->{open_elements}}) {
8015              my $node = $self->{open_elements}->[$_];
8016              if ($node->[1] & FOREIGN_EL) {
8017                last INSCOPE;
8018              } elsif ($node->[1] & SCOPING_EL) {
8019                last;
8020              }
8021            }
8022            
8023            ## NOTE: No foreign element in scope.
8024            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8025          } # INSCOPE
8026        }
8027    } # B    } # B
8028    
   ## NOTE: The "trailing end" phase in HTML5 is split into  
   ## two insertion modes: "after html body" and "after html frameset".  
   ## NOTE: States in the main stage is preserved while  
   ## the parser stays in the trailing end phase. # MUST  
   
8029    ## Stop parsing # MUST    ## Stop parsing # MUST
8030        
8031    ## TODO: script stuffs    ## TODO: script stuffs
8032  } # _tree_construct_main  } # _tree_construct_main
8033    
8034  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8035    my $class = shift;    my $class = shift;
8036    my $node = shift;    my $node = shift;
8037    my $s = \$_[0];    #my $s = \$_[0];
8038    my $onerror = $_[1];    my $onerror = $_[1];
8039      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8040    
8041    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8042    
# Line 5341  sub set_inner_html ($$$) { Line 8055  sub set_inner_html ($$$) {
8055      }      }
8056    
8057      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8058      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8059    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8060      ## TODO: If non-html element      ## TODO: If non-html element
8061    
8062      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8063    
8064    ## TODO: Support for $get_wrapper
8065    
8066      ## Step 1 # MUST      ## Step 1 # MUST
8067      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8068      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5354  sub set_inner_html ($$$) { Line 8070  sub set_inner_html ($$$) {
8070      my $p = $class->new;      my $p = $class->new;
8071      $p->{document} = $doc;      $p->{document} = $doc;
8072    
8073      ## Step 9 # MUST      ## Step 8 # MUST
8074      my $i = 0;      my $i = 0;
8075      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8076      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8077      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8078        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8079        $input = $get_wrapper->($input);
8080        $p->{set_nc} = sub {
8081        my $self = shift;        my $self = shift;
8082    
8083        pop @{$self->{prev_input_character}};        my $char = '';
8084        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8085            $char = $self->{next_nc};
8086            delete $self->{next_nc};
8087            $self->{nc} = ord $char;
8088          } else {
8089            $self->{char_buffer} = '';
8090            $self->{char_buffer_pos} = 0;
8091            
8092            my $count = $input->manakai_read_until
8093                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8094                 $self->{char_buffer_pos});
8095            if ($count) {
8096              $self->{line_prev} = $self->{line};
8097              $self->{column_prev} = $self->{column};
8098              $self->{column}++;
8099              $self->{nc}
8100                  = ord substr ($self->{char_buffer},
8101                                $self->{char_buffer_pos}++, 1);
8102              return;
8103            }
8104            
8105            if ($input->read ($char, 1)) {
8106              $self->{nc} = ord $char;
8107            } else {
8108              $self->{nc} = -1;
8109              return;
8110            }
8111          }
8112    
8113        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8114        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
8115        $column++;  
8116          if ($self->{nc} == 0x000A) { # LF
8117        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
8118          $line++;          $p->{column} = 0;
8119          $column = 0;          !!!cp ('i1');
8120        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8121          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8122          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
8123          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8124          $column = 0;            $self->{next_nc} = $next;
8125        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
8126          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
8127        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
8128            $p->{column} = 0;
8129            !!!cp ('i2');
8130          } elsif ($self->{nc} == 0x0000) { # NULL
8131            !!!cp ('i4');
8132          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8133          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8134        }        }
8135      };      };
8136      $p->{prev_input_character} = [-1, -1, -1];  
8137      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8138              #my ($scalar, $specials_range, $offset) = @_;
8139          return 0 if defined $p->{next_nc};
8140    
8141          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8142          my $offset = $_[2] || 0;
8143          
8144          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8145            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8146            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8147              substr ($_[0], $offset)
8148                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8149              my $count = $+[0] - $-[0];
8150              if ($count) {
8151                $p->{column} += $count;
8152                $p->{char_buffer_pos} += $count;
8153                $p->{line_prev} = $p->{line};
8154                $p->{column_prev} = $p->{column} - 1;
8155                $p->{nc} = -1;
8156              }
8157              return $count;
8158            } else {
8159              return 0;
8160            }
8161          } else {
8162            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8163            if ($count) {
8164              $p->{column} += $count;
8165              $p->{column_prev} += $count;
8166              $p->{nc} = -1;
8167            }
8168            return $count;
8169          }
8170        }; # $p->{read_until}
8171    
8172      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8173        my (%opt) = @_;        my (%opt) = @_;
8174        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8175          my $column = $opt{column};
8176          if (defined $opt{token} and defined $opt{token}->{line}) {
8177            $line = $opt{token}->{line};
8178            $column = $opt{token}->{column};
8179          }
8180          warn "Parse error ($opt{type}) at line $line column $column\n";
8181      };      };
8182      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8183        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8184      };      };
8185            
8186        my $char_onerror = sub {
8187          my (undef, $type, %opt) = @_;
8188          $ponerror->(layer => 'encode',
8189                      line => $p->{line}, column => $p->{column} + 1,
8190                      %opt, type => $type);
8191        }; # $char_onerror
8192        $input->onerror ($char_onerror);
8193    
8194      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8195      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8196    
8197      ## Step 2      ## Step 2
8198      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8199      $p->{content_model} = {      $p->{content_model} = {
8200        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
8201        textarea => RCDATA_CONTENT_MODEL,        textarea => RCDATA_CONTENT_MODEL,
# Line 5415  sub set_inner_html ($$$) { Line 8212  sub set_inner_html ($$$) {
8212          unless defined $p->{content_model};          unless defined $p->{content_model};
8213          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8214    
8215      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8216          ## TODO: Foreign element OK?
8217    
8218      ## Step 4      ## Step 3
8219      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8220        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8221    
8222      ## Step 5 # MUST      ## Step 4 # MUST
8223      $doc->append_child ($root);      $doc->append_child ($root);
8224    
8225      ## Step 6 # MUST      ## Step 5 # MUST
8226      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8227    
8228      undef $p->{head_element};      undef $p->{head_element};
8229        undef $p->{head_element_inserted};
8230    
8231      ## Step 7 # MUST      ## Step 6 # MUST
8232      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8233    
8234      ## Step 8 # MUST      ## Step 7 # MUST
8235      my $anode = $node;      my $anode = $node;
8236      AN: while (defined $anode) {      AN: while (defined $anode) {
8237        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8238          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8239          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8240            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8241                !!!cp ('i5');
8242              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8243              last AN;              last AN;
8244            }            }
# Line 5447  sub set_inner_html ($$$) { Line 8247  sub set_inner_html ($$$) {
8247        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8248      } # AN      } # AN
8249            
8250      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8251      {      {
8252        my $self = $p;        my $self = $p;
8253        !!!next-token;        !!!next-token;
8254      }      }
8255      $p->_tree_construction_main;      $p->_tree_construction_main;
8256    
8257      ## Step 11 # MUST      ## Step 10 # MUST
8258      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8259      for (@cn) {      for (@cn) {
8260        $node->remove_child ($_);        $node->remove_child ($_);
8261      }      }
8262      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8263    
8264      ## Step 12 # MUST      ## Step 11 # MUST
8265      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8266      for (@cn) {      for (@cn) {
8267        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5471  sub set_inner_html ($$$) { Line 8270  sub set_inner_html ($$$) {
8270      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8271    
8272      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8273    
8274        delete $p->{parse_error}; # delete loop
8275    } else {    } else {
8276      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";
8277    }    }

Legend:
Removed from v.1.67  
changed lines
  Added in v.1.202

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24