/[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.77 by wakaba, Mon Mar 3 10:20:19 2008 UTC revision 1.205 by wakaba, Mon Oct 13 06:18:31 2008 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## TODO: Control charcters and noncharacters are not allowed (HTML5 revision 1263)  require IO::Handle;
23  ## TODO: 1252 parse error (revision 1264)  
24  ## TODO: 8859-11 = 874 (revision 1271)  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  my $permitted_slash_tag_name = {  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    base => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    link => 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    meta => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    hr => 1,  
31    br => 1,  sub A_EL () { 0b1 }
32    img => 1,  sub ADDRESS_EL () { 0b10 }
33    embed => 1,  sub BODY_EL () { 0b100 }
34    param => 1,  sub BUTTON_EL () { 0b1000 }
35    area => 1,  sub CAPTION_EL () { 0b10000 }
36    col => 1,  sub DD_EL () { 0b100000 }
37    input => 1,  sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      ## ISSUE: option, optgroup, rt, rp?
90    
91      BODY_EL |
92      HTML_EL |
93      TABLE_CELL_EL |
94      TABLE_ROW_EL |
95      TABLE_ROW_GROUP_EL
96    }
97    
98    sub SCOPING_EL () {
99      BUTTON_EL |
100      CAPTION_EL |
101      HTML_EL |
102      TABLE_EL |
103      TABLE_CELL_EL |
104      MISC_SCOPING_EL
105    }
106    
107    sub TABLE_SCOPING_EL () {
108      HTML_EL |
109      TABLE_EL
110    }
111    
112    sub TABLE_ROWS_SCOPING_EL () {
113      HTML_EL |
114      TABLE_ROW_GROUP_EL
115    }
116    
117    sub TABLE_ROW_SCOPING_EL () {
118      HTML_EL |
119      TABLE_ROW_EL
120    }
121    
122    sub SPECIAL_EL () {
123      ADDRESS_EL |
124      BODY_EL |
125      DIV_EL |
126    
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
132      FORM_EL |
133      FRAMESET_EL |
134      HEADING_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240    };
241    
242    my $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322    };
323    
324    my $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 59  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
 };  
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
387    
388  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
398    my $charset = shift;    my $charset_name = shift;
399    my $bytes_s = ref $_[0] ? $_[0] : \($_[0]);    my $byte_stream = $_[0];
   my $s;  
     
   if (defined $charset) {  
     require Encode; ## TODO: decode(utf8) don't delete BOM  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = lc $charset; ## TODO: normalize name  
     $self->{confident} = 1;  
   } else {  
     ## TODO: Implement HTML5 detection algorithm  
     require Whatpm::Charset::UniversalCharDet;  
     $charset = Whatpm::Charset::UniversalCharDet->detect_byte_string  
         (substr ($$bytes_s, 0, 1024));  
     $charset ||= 'windows-1252';  
     $s = \ (Encode::decode ($charset, $$bytes_s));  
     $self->{input_encoding} = $charset;  
     $self->{confident} = 0;  
   }  
400    
401    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
402      my $self = shift;      my (%opt) = @_;
403      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
404      ## TODO: if $charset is supported    };
405      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      ## Step 1        };
410      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
411        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444      }      }
445    
446      ## Step 2      ## Step 2
447      if (defined $self->{input_encoding} and      my $byte_buffer = '';
448          $self->{input_encoding} eq $charset) {      for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460        $self->{confident} = 1;        $self->{confident} = 1;
461        return;        last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476      }      }
477    
478      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
479          ':'.$charset, level => 'w');      ## TODO: <meta charset>
480    
481      ## Step 3      ## Step 5
482      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
483    
484      ## Step 4      ## Step 6
485      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599    }; # $self->{change_encoding}    }; # $self->{change_encoding}
600    
601    my @args = @_; shift @args; # $s    my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615    my $return;    my $return;
616    try {    try {
617      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
619      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
620      $s = \ (Encode::decode ($charset, $$bytes_s));      
621      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639      $self->{confident} = 1;      $self->{confident} = 1;
640      $return = $self->parse_char_string ($s, @args);  
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645    };    };
646    return $return;    return $return;
647  } # parse_byte_string  } # parse_byte_stream
648    
649  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650  ## and the HTML layer MUST ignore it.  However, we does strip BOM in  ## and the HTML layer MUST ignore it.  However, we does strip BOM in
# Line 162  sub parse_byte_string ($$$$;$) { Line 655  sub parse_byte_string ($$$$;$) {
655  ## such as |parse_byte_string| in this module, must ensure that it does  ## such as |parse_byte_string| in this module, must ensure that it does
656  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
657    
658  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
669    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
670    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
673    
# Line 175  sub parse_string ($$$;$) { Line 676  sub parse_string ($$$;$) {
676    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
677    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
678        if defined $self->{input_encoding};        if defined $self->{input_encoding};
679    ## TODO: |{input_encoding}| is needless?
680    
681    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
682    my $line = 1;    $self->{column_prev} = -1;
683    my $column = 0;    $self->{column} = 0;
684    $self->{set_next_char} = sub {    $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_char}};      my $char = '';
688      unshift @{$self->{prev_char}}, $self->{next_char};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707      $self->{next_char} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
708      $self->{next_char} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
709      $column++;        } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_char} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_char} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_char} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_char} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_char} == 0x0000) { # NULL        }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_char} = [-1, -1, -1];  
740    $self->{next_char} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_char} = sub {      level => {must => 'm',
814      $self->{next_char} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 844  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
844  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
847  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
848  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
849  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
850  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 855  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
855  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
861  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 877  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
877  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
879  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
902  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 913  sub TABLE_IMS ()      { 0b1000000 }
913  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
914  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
915  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
922        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
923        ## combined with the original insertion mode.  In thie parser,
924        ## they are stored together in the bit-or'ed form.
925    
926    ## NOTE: "initial" and "before html" insertion modes have no constants.
927    
928    ## NOTE: "after after body" insertion mode.
929  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
930    
931    ## NOTE: "after after frameset" insertion mode.
932  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
933    
934  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
935  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
936  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 319  sub IN_TABLE_IM () { TABLE_IMS } Line 944  sub IN_TABLE_IM () { TABLE_IMS }
944  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
945  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
946  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
947  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
948    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
949  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
950    
951  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
# Line 327  sub IN_COLUMN_GROUP_IM () { 0b10 } Line 953  sub IN_COLUMN_GROUP_IM () { 0b10 }
953  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
954    my $self = shift;    my $self = shift;
955    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
956      #$self->{s_kwd}; # state keyword - initialized when used
957      #$self->{entity__value}; # initialized when used
958      #$self->{entity__match}; # initialized when used
959    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
960    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
961    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
962    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
963    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
964    $self->{char} = [];    delete $self->{self_closing};
965    # $self->{next_char}    $self->{char_buffer} = '';
966      $self->{char_buffer_pos} = 0;
967      $self->{nc} = -1; # next input character
968      #$self->{next_nc}
969    !!!next-input-character;    !!!next-input-character;
970    $self->{token} = [];    $self->{token} = [];
971    # $self->{escape}    # $self->{escape}
# Line 344  sub _initialize_tokenizer ($) { Line 976  sub _initialize_tokenizer ($) {
976  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
977  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
978  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
979  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
980  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
981  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
982  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
983  ##        ->{name}  ##        ->{name}
984  ##        ->{value}  ##        ->{value}
985  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
986  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
987    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
988    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
989    ##     while the token is pushed back to the stack.
990    
991  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
992    
# Line 361  sub _initialize_tokenizer ($) { Line 996  sub _initialize_tokenizer ($) {
996  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
997  ## and removed from the list.  ## and removed from the list.
998    
999  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1000  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1001  ## contains some requirements that are not detected by the  
1002  ## parsing algorithm:  my $is_space = {
1003  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1004  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1005  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1006  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1007  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1008  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1009    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1010    
1011  sub _get_next_token ($) {  sub _get_next_token ($) {
1012    my $self = shift;    my $self = shift;
1013    
1014      if ($self->{self_closing}) {
1015        !!!parse-error (type => 'nestc', token => $self->{ct});
1016        ## NOTE: The |self_closing| flag is only set by start tag token.
1017        ## In addition, when a start tag token is emitted, it is always set to
1018        ## |ct|.
1019        delete $self->{self_closing};
1020      }
1021    
1022    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1023        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1024      return shift @{$self->{token}};      return shift @{$self->{token}};
1025    }    }
1026    
1027    A: {    A: {
1028      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1029        if ($self->{next_char} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1030    
1031          if ($self->{nc} == 0x0026) { # &
1032            !!!cp (0.1);
1033            ## NOTE: In the spec, the tokenizer is switched to the
1034            ## "entity data state".  In this implementation, the tokenizer
1035            ## is switched to the |ENTITY_STATE|, which is an implementation
1036            ## of the "consume a character reference" algorithm.
1037            $self->{entity_add} = -1;
1038            $self->{prev_state} = DATA_STATE;
1039            $self->{state} = ENTITY_STATE;
1040            !!!next-input-character;
1041            redo A;
1042          } elsif ($self->{nc} == 0x003C) { # <
1043            !!!cp (0.2);
1044            $self->{state} = TAG_OPEN_STATE;
1045            !!!next-input-character;
1046            redo A;
1047          } elsif ($self->{nc} == -1) {
1048            !!!cp (0.3);
1049            !!!emit ({type => END_OF_FILE_TOKEN,
1050                      line => $self->{line}, column => $self->{column}});
1051            last A; ## TODO: ok?
1052          } else {
1053            !!!cp (0.4);
1054            #
1055          }
1056    
1057          # Anything else
1058          my $token = {type => CHARACTER_TOKEN,
1059                       data => chr $self->{nc},
1060                       line => $self->{line}, column => $self->{column},
1061                      };
1062          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1063    
1064          ## Stay in the state.
1065          !!!next-input-character;
1066          !!!emit ($token);
1067          redo A;
1068        } elsif ($self->{state} == DATA_STATE) {
1069          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1070          if ($self->{nc} == 0x0026) { # &
1071            $self->{s_kwd} = '';
1072          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1073              not $self->{escape}) {              not $self->{escape}) {
1074            !!!cp (1);            !!!cp (1);
1075            $self->{state} = ENTITY_DATA_STATE;            ## NOTE: In the spec, the tokenizer is switched to the
1076              ## "entity data state".  In this implementation, the tokenizer
1077              ## is switched to the |ENTITY_STATE|, which is an implementation
1078              ## of the "consume a character reference" algorithm.
1079              $self->{entity_add} = -1;
1080              $self->{prev_state} = DATA_STATE;
1081              $self->{state} = ENTITY_STATE;
1082            !!!next-input-character;            !!!next-input-character;
1083            redo A;            redo A;
1084          } else {          } else {
1085            !!!cp (2);            !!!cp (2);
1086            #            #
1087          }          }
1088        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1089          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1090            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1091              if ($self->{prev_char}->[0] == 0x002D and # -            
1092                  $self->{prev_char}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1093                  $self->{prev_char}->[2] == 0x003C) { # <              !!!cp (3);
1094                !!!cp (3);              $self->{escape} = 1; # unless $self->{escape};
1095                $self->{escape} = 1;              $self->{s_kwd} = '--';
1096              } else {              #
1097                !!!cp (4);            } elsif ($self->{s_kwd} eq '---') {
1098              }              !!!cp (4);
1099                $self->{s_kwd} = '--';
1100                #
1101            } else {            } else {
1102              !!!cp (5);              !!!cp (5);
1103                #
1104            }            }
1105          }          }
1106                    
1107          #          #
1108        } elsif ($self->{next_char} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1109            if (length $self->{s_kwd}) {
1110              !!!cp (5.1);
1111              $self->{s_kwd} .= '!';
1112              #
1113            } else {
1114              !!!cp (5.2);
1115              #$self->{s_kwd} = '';
1116              #
1117            }
1118            #
1119          } elsif ($self->{nc} == 0x003C) { # <
1120          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1121              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1122               not $self->{escape})) {               not $self->{escape})) {
# Line 422  sub _get_next_token ($) { Line 1126  sub _get_next_token ($) {
1126            redo A;            redo A;
1127          } else {          } else {
1128            !!!cp (7);            !!!cp (7);
1129              $self->{s_kwd} = '';
1130            #            #
1131          }          }
1132        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1133          if ($self->{escape} and          if ($self->{escape} and
1134              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1135            if ($self->{prev_char}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
               $self->{prev_char}->[1] == 0x002D) { # -  
1136              !!!cp (8);              !!!cp (8);
1137              delete $self->{escape};              delete $self->{escape};
1138            } else {            } else {
# Line 438  sub _get_next_token ($) { Line 1142  sub _get_next_token ($) {
1142            !!!cp (10);            !!!cp (10);
1143          }          }
1144                    
1145            $self->{s_kwd} = '';
1146          #          #
1147        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1148          !!!cp (11);          !!!cp (11);
1149          !!!emit ({type => END_OF_FILE_TOKEN});          $self->{s_kwd} = '';
1150            !!!emit ({type => END_OF_FILE_TOKEN,
1151                      line => $self->{line}, column => $self->{column}});
1152          last A; ## TODO: ok?          last A; ## TODO: ok?
1153        } else {        } else {
1154          !!!cp (12);          !!!cp (12);
1155            $self->{s_kwd} = '';
1156            #
1157        }        }
1158    
1159        # Anything else        # Anything else
1160        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1161                     data => chr $self->{next_char}};                     data => chr $self->{nc},
1162        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1163        !!!next-input-character;                    };
1164          if ($self->{read_until}->($token->{data}, q[-!<>&],
1165        !!!emit ($token);                                  length $token->{data})) {
1166            $self->{s_kwd} = '';
1167        redo A;        }
     } elsif ($self->{state} == ENTITY_DATA_STATE) {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);  
   
       $self->{state} = DATA_STATE;  
       # next-input-character is already done  
1168    
1169        unless (defined $token) {        ## Stay in the data state.
1170          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1171          !!!cp (13);          !!!cp (13);
1172          !!!emit ({type => CHARACTER_TOKEN, data => '&'});          $self->{state} = PCDATA_STATE;
1173        } else {        } else {
1174          !!!cp (14);          !!!cp (14);
1175          !!!emit ($token);          ## Stay in the state.
1176        }        }
1177          !!!next-input-character;
1178          !!!emit ($token);
1179        redo A;        redo A;
1180      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1181        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1182          if ($self->{next_char} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1183            !!!cp (15);            !!!cp (15);
1184            !!!next-input-character;            !!!next-input-character;
1185            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1186            redo A;            redo A;
1187            } elsif ($self->{nc} == 0x0021) { # !
1188              !!!cp (15.1);
1189              $self->{s_kwd} = '<' unless $self->{escape};
1190              #
1191          } else {          } else {
1192            !!!cp (16);            !!!cp (16);
1193            ## reconsume            #
           $self->{state} = DATA_STATE;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1194          }          }
1195    
1196            ## reconsume
1197            $self->{state} = DATA_STATE;
1198            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1199                      line => $self->{line_prev},
1200                      column => $self->{column_prev},
1201                     });
1202            redo A;
1203        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1204          if ($self->{next_char} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1205            !!!cp (17);            !!!cp (17);
1206            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1207            !!!next-input-character;            !!!next-input-character;
1208            redo A;            redo A;
1209          } elsif ($self->{next_char} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1210            !!!cp (18);            !!!cp (18);
1211            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1212            !!!next-input-character;            !!!next-input-character;
1213            redo A;            redo A;
1214          } elsif (0x0041 <= $self->{next_char} and          } elsif (0x0041 <= $self->{nc} and
1215                   $self->{next_char} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1216            !!!cp (19);            !!!cp (19);
1217            $self->{current_token}            $self->{ct}
1218              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1219                 tag_name => chr ($self->{next_char} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1220                   line => $self->{line_prev},
1221                   column => $self->{column_prev}};
1222            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1223            !!!next-input-character;            !!!next-input-character;
1224            redo A;            redo A;
1225          } elsif (0x0061 <= $self->{next_char} and          } elsif (0x0061 <= $self->{nc} and
1226                   $self->{next_char} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1227            !!!cp (20);            !!!cp (20);
1228            $self->{current_token} = {type => START_TAG_TOKEN,            $self->{ct} = {type => START_TAG_TOKEN,
1229                              tag_name => chr ($self->{next_char})};                                      tag_name => chr ($self->{nc}),
1230                                        line => $self->{line_prev},
1231                                        column => $self->{column_prev}};
1232            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1233            !!!next-input-character;            !!!next-input-character;
1234            redo A;            redo A;
1235          } elsif ($self->{next_char} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1236            !!!cp (21);            !!!cp (21);
1237            !!!parse-error (type => 'empty start tag');            !!!parse-error (type => 'empty start tag',
1238                              line => $self->{line_prev},
1239                              column => $self->{column_prev});
1240            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1241            !!!next-input-character;            !!!next-input-character;
1242    
1243            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1244                        line => $self->{line_prev},
1245                        column => $self->{column_prev},
1246                       });
1247    
1248            redo A;            redo A;
1249          } elsif ($self->{next_char} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1250            !!!cp (22);            !!!cp (22);
1251            !!!parse-error (type => 'pio');            !!!parse-error (type => 'pio',
1252                              line => $self->{line_prev},
1253                              column => $self->{column_prev});
1254            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1255            ## $self->{next_char} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1256                                        line => $self->{line_prev},
1257                                        column => $self->{column_prev},
1258                                       };
1259              ## $self->{nc} is intentionally left as is
1260            redo A;            redo A;
1261          } else {          } else {
1262            !!!cp (23);            !!!cp (23);
1263            !!!parse-error (type => 'bare stago');            !!!parse-error (type => 'bare stago',
1264                              line => $self->{line_prev},
1265                              column => $self->{column_prev});
1266            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1267            ## reconsume            ## reconsume
1268    
1269            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1270                        line => $self->{line_prev},
1271                        column => $self->{column_prev},
1272                       });
1273    
1274            redo A;            redo A;
1275          }          }
# Line 545  sub _get_next_token ($) { Line 1277  sub _get_next_token ($) {
1277          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1278        }        }
1279      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1280        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1281          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_char};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_char} == $c or $self->{next_char} == $C) {  
               !!!cp (24);  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               !!!cp (25);  
               $self->{next_char} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
1282    
1283                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1284            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1285                redo A;          if (defined $self->{last_stag_name}) {
1286              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1287            }            $self->{s_kwd} = '';
1288            push @next_char, $self->{next_char};            ## Reconsume.
1289                    redo A;
           unless ($self->{next_char} == 0x0009 or # HT  
                   $self->{next_char} == 0x000A or # LF  
                   $self->{next_char} == 0x000B or # VT  
                   $self->{next_char} == 0x000C or # FF  
                   $self->{next_char} == 0x0020 or # SP  
                   $self->{next_char} == 0x003E or # >  
                   $self->{next_char} == 0x002F or # /  
                   $self->{next_char} == -1) {  
             !!!cp (26);  
             $self->{next_char} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             !!!cp (27);  
             $self->{next_char} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1290          } else {          } else {
1291            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1292              ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1293            !!!cp (28);            !!!cp (28);
           # next-input-character is already done  
1294            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1295            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1296              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1297                        line => $l, column => $c,
1298                       });
1299            redo A;            redo A;
1300          }          }
1301        }        }
1302          
1303        if (0x0041 <= $self->{next_char} and        if (0x0041 <= $self->{nc} and
1304            $self->{next_char} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1305          !!!cp (29);          !!!cp (29);
1306          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct}
1307                            tag_name => chr ($self->{next_char} + 0x0020)};              = {type => END_TAG_TOKEN,
1308                   tag_name => chr ($self->{nc} + 0x0020),
1309                   line => $l, column => $c};
1310          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1311          !!!next-input-character;          !!!next-input-character;
1312          redo A;          redo A;
1313        } elsif (0x0061 <= $self->{next_char} and        } elsif (0x0061 <= $self->{nc} and
1314                 $self->{next_char} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1315          !!!cp (30);          !!!cp (30);
1316          $self->{current_token} = {type => END_TAG_TOKEN,          $self->{ct} = {type => END_TAG_TOKEN,
1317                            tag_name => chr ($self->{next_char})};                                    tag_name => chr ($self->{nc}),
1318                                      line => $l, column => $c};
1319          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1320          !!!next-input-character;          !!!next-input-character;
1321          redo A;          redo A;
1322        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1323          !!!cp (31);          !!!cp (31);
1324          !!!parse-error (type => 'empty end tag');          !!!parse-error (type => 'empty end tag',
1325                            line => $self->{line_prev}, ## "<" in "</>"
1326                            column => $self->{column_prev} - 1);
1327          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1328          !!!next-input-character;          !!!next-input-character;
1329          redo A;          redo A;
1330        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1331          !!!cp (32);          !!!cp (32);
1332          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1333          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1334          # reconsume          # reconsume
1335    
1336          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1337                      line => $l, column => $c,
1338                     });
1339    
1340          redo A;          redo A;
1341        } else {        } else {
1342          !!!cp (33);          !!!cp (33);
1343          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1344          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1345          ## $self->{next_char} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1346          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1347                                      column => $self->{column_prev} - 1,
1348                                     };
1349            ## NOTE: $self->{nc} is intentionally left as is.
1350            ## Although the "anything else" case of the spec not explicitly
1351            ## states that the next input character is to be reconsumed,
1352            ## it will be included to the |data| of the comment token
1353            ## generated from the bogus end tag, as defined in the
1354            ## "bogus comment state" entry.
1355            redo A;
1356          }
1357        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1358          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1359          if (length $ch) {
1360            my $CH = $ch;
1361            $ch =~ tr/a-z/A-Z/;
1362            my $nch = chr $self->{nc};
1363            if ($nch eq $ch or $nch eq $CH) {
1364              !!!cp (24);
1365              ## Stay in the state.
1366              $self->{s_kwd} .= $nch;
1367              !!!next-input-character;
1368              redo A;
1369            } else {
1370              !!!cp (25);
1371              $self->{state} = DATA_STATE;
1372              ## Reconsume.
1373              !!!emit ({type => CHARACTER_TOKEN,
1374                        data => '</' . $self->{s_kwd},
1375                        line => $self->{line_prev},
1376                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1377                       });
1378              redo A;
1379            }
1380          } else { # after "<{tag-name}"
1381            unless ($is_space->{$self->{nc}} or
1382                    {
1383                     0x003E => 1, # >
1384                     0x002F => 1, # /
1385                     -1 => 1, # EOF
1386                    }->{$self->{nc}}) {
1387              !!!cp (26);
1388              ## Reconsume.
1389              $self->{state} = DATA_STATE;
1390              !!!emit ({type => CHARACTER_TOKEN,
1391                        data => '</' . $self->{s_kwd},
1392                        line => $self->{line_prev},
1393                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1394                       });
1395              redo A;
1396            } else {
1397              !!!cp (27);
1398              $self->{ct}
1399                  = {type => END_TAG_TOKEN,
1400                     tag_name => $self->{last_stag_name},
1401                     line => $self->{line_prev},
1402                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1403              $self->{state} = TAG_NAME_STATE;
1404              ## Reconsume.
1405              redo A;
1406            }
1407        }        }
1408      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1409        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1410          !!!cp (34);          !!!cp (34);
1411          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1412          !!!next-input-character;          !!!next-input-character;
1413          redo A;          redo A;
1414        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1415          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1416            !!!cp (35);            !!!cp (35);
1417            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1418                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1419            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1420            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1421              !!!cp (36);            #  ## NOTE: This should never be reached.
1422              !!!parse-error (type => 'end tag attribute');            #  !!! cp (36);
1423            } else {            #  !!! parse-error (type => 'end tag attribute');
1424              #} else {
1425              !!!cp (37);              !!!cp (37);
1426            }            #}
1427          } else {          } else {
1428            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1429          }          }
1430          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1431          !!!next-input-character;          !!!next-input-character;
1432    
1433          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1434    
1435          redo A;          redo A;
1436        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1437                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1438          !!!cp (38);          !!!cp (38);
1439          $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);          $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1440            # start tag or end tag            # start tag or end tag
1441          ## Stay in this state          ## Stay in this state
1442          !!!next-input-character;          !!!next-input-character;
1443          redo A;          redo A;
1444        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1445          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1446          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1447            !!!cp (39);            !!!cp (39);
1448            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1449                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1450            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1451            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1452              !!!cp (40);            #  ## NOTE: This state should never be reached.
1453              !!!parse-error (type => 'end tag attribute');            #  !!! cp (40);
1454            } else {            #  !!! parse-error (type => 'end tag attribute');
1455              #} else {
1456              !!!cp (41);              !!!cp (41);
1457            }            #}
1458          } else {          } else {
1459            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1460          }          }
1461          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1462          # reconsume          # reconsume
1463    
1464          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1465    
1466          redo A;          redo A;
1467        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1468            !!!cp (42);
1469            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1470          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (42);  
           #  
         } else {  
           !!!cp (43);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1471          redo A;          redo A;
1472        } else {        } else {
1473          !!!cp (44);          !!!cp (44);
1474          $self->{current_token}->{tag_name} .= chr $self->{next_char};          $self->{ct}->{tag_name} .= chr $self->{nc};
1475            # start tag or end tag            # start tag or end tag
1476          ## Stay in the state          ## Stay in the state
1477          !!!next-input-character;          !!!next-input-character;
1478          redo A;          redo A;
1479        }        }
1480      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1481        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1482          !!!cp (45);          !!!cp (45);
1483          ## Stay in the state          ## Stay in the state
1484          !!!next-input-character;          !!!next-input-character;
1485          redo A;          redo A;
1486        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1487          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1488            !!!cp (46);            !!!cp (46);
1489            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1490                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1491            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1492            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1493              !!!cp (47);              !!!cp (47);
1494              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1495            } else {            } else {
1496              !!!cp (48);              !!!cp (48);
1497            }            }
1498          } else {          } else {
1499            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1500          }          }
1501          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1502          !!!next-input-character;          !!!next-input-character;
1503    
1504          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1505    
1506          redo A;          redo A;
1507        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1508                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1509          !!!cp (49);          !!!cp (49);
1510          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1511                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1512                   value => '',
1513                   line => $self->{line}, column => $self->{column}};
1514          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1515          !!!next-input-character;          !!!next-input-character;
1516          redo A;          redo A;
1517        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1518            !!!cp (50);
1519            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1520          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (50);  
           #  
         } else {  
           !!!cp (51);  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1521          redo A;          redo A;
1522        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1523          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1524          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1525            !!!cp (52);            !!!cp (52);
1526            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1527                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1528            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1529            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1530              !!!cp (53);              !!!cp (53);
1531              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1532            } else {            } else {
1533              !!!cp (54);              !!!cp (54);
1534            }            }
1535          } else {          } else {
1536            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1537          }          }
1538          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1539          # reconsume          # reconsume
1540    
1541          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1542    
1543          redo A;          redo A;
1544        } else {        } else {
# Line 811  sub _get_next_token ($) { Line 1546  sub _get_next_token ($) {
1546               0x0022 => 1, # "               0x0022 => 1, # "
1547               0x0027 => 1, # '               0x0027 => 1, # '
1548               0x003D => 1, # =               0x003D => 1, # =
1549              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1550            !!!cp (55);            !!!cp (55);
1551            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1552          } else {          } else {
1553            !!!cp (56);            !!!cp (56);
1554          }          }
1555          $self->{current_attribute} = {name => chr ($self->{next_char}),          $self->{ca}
1556                                value => ''};              = {name => chr ($self->{nc}),
1557                   value => '',
1558                   line => $self->{line}, column => $self->{column}};
1559          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1560          !!!next-input-character;          !!!next-input-character;
1561          redo A;          redo A;
1562        }        }
1563      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1564        my $before_leave = sub {        my $before_leave = sub {
1565          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1566              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1567            !!!cp (57);            !!!cp (57);
1568            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1569            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{ca} # MUST
1570          } else {          } else {
1571            !!!cp (58);            !!!cp (58);
1572            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1573              = $self->{current_attribute};              = $self->{ca};
1574          }          }
1575        }; # $before_leave        }; # $before_leave
1576    
1577        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1578          !!!cp (59);          !!!cp (59);
1579          $before_leave->();          $before_leave->();
1580          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1581          !!!next-input-character;          !!!next-input-character;
1582          redo A;          redo A;
1583        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1584          !!!cp (60);          !!!cp (60);
1585          $before_leave->();          $before_leave->();
1586          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1587          !!!next-input-character;          !!!next-input-character;
1588          redo A;          redo A;
1589        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1590          $before_leave->();          $before_leave->();
1591          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1592            !!!cp (61);            !!!cp (61);
1593            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1594                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1595            !!!cp (62);            !!!cp (62);
1596            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1597            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1598              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1599            }            }
1600          } else {          } else {
1601            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1602          }          }
1603          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1604          !!!next-input-character;          !!!next-input-character;
1605    
1606          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1607    
1608          redo A;          redo A;
1609        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1610                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1611          !!!cp (63);          !!!cp (63);
1612          $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);          $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1613          ## Stay in the state          ## Stay in the state
1614          !!!next-input-character;          !!!next-input-character;
1615          redo A;          redo A;
1616        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1617            !!!cp (64);
1618          $before_leave->();          $before_leave->();
1619            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1620          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (64);  
           #  
         } else {  
           !!!cp (65);  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1621          redo A;          redo A;
1622        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1623          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1624          $before_leave->();          $before_leave->();
1625          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1626            !!!cp (66);            !!!cp (66);
1627            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1628                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1629            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1630            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1631              !!!cp (67);              !!!cp (67);
1632              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1633            } else {            } else {
1634                ## NOTE: This state should never be reached.
1635              !!!cp (68);              !!!cp (68);
1636            }            }
1637          } else {          } else {
1638            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1639          }          }
1640          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1641          # reconsume          # reconsume
1642    
1643          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1644    
1645          redo A;          redo A;
1646        } else {        } else {
1647          if ($self->{next_char} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1648              $self->{next_char} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1649            !!!cp (69);            !!!cp (69);
1650            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1651          } else {          } else {
1652            !!!cp (70);            !!!cp (70);
1653          }          }
1654          $self->{current_attribute}->{name} .= chr ($self->{next_char});          $self->{ca}->{name} .= chr ($self->{nc});
1655          ## Stay in the state          ## Stay in the state
1656          !!!next-input-character;          !!!next-input-character;
1657          redo A;          redo A;
1658        }        }
1659      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1660        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1661          !!!cp (71);          !!!cp (71);
1662          ## Stay in the state          ## Stay in the state
1663          !!!next-input-character;          !!!next-input-character;
1664          redo A;          redo A;
1665        } elsif ($self->{next_char} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1666          !!!cp (72);          !!!cp (72);
1667          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1668          !!!next-input-character;          !!!next-input-character;
1669          redo A;          redo A;
1670        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1671          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1672            !!!cp (73);            !!!cp (73);
1673            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1674                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1675            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1676            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1677              !!!cp (74);              !!!cp (74);
1678              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1679            } else {            } else {
1680                ## NOTE: This state should never be reached.
1681              !!!cp (75);              !!!cp (75);
1682            }            }
1683          } else {          } else {
1684            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1685          }          }
1686          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1687          !!!next-input-character;          !!!next-input-character;
1688    
1689          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1690    
1691          redo A;          redo A;
1692        } elsif (0x0041 <= $self->{next_char} and        } elsif (0x0041 <= $self->{nc} and
1693                 $self->{next_char} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1694          !!!cp (76);          !!!cp (76);
1695          $self->{current_attribute} = {name => chr ($self->{next_char} + 0x0020),          $self->{ca}
1696                                value => ''};              = {name => chr ($self->{nc} + 0x0020),
1697                   value => '',
1698                   line => $self->{line}, column => $self->{column}};
1699          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1700          !!!next-input-character;          !!!next-input-character;
1701          redo A;          redo A;
1702        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1703            !!!cp (77);
1704            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1705          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_char} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           !!!cp (77);  
           #  
         } else {  
           !!!cp (78);  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1706          redo A;          redo A;
1707        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1708          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1709          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1710            !!!cp (79);            !!!cp (79);
1711            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1712                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1713            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1714            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1715              !!!cp (80);              !!!cp (80);
1716              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1717            } else {            } else {
1718                ## NOTE: This state should never be reached.
1719              !!!cp (81);              !!!cp (81);
1720            }            }
1721          } else {          } else {
1722            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1723          }          }
1724          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1725          # reconsume          # reconsume
1726    
1727          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1728    
1729          redo A;          redo A;
1730        } else {        } else {
1731          !!!cp (82);          if ($self->{nc} == 0x0022 or # "
1732          $self->{current_attribute} = {name => chr ($self->{next_char}),              $self->{nc} == 0x0027) { # '
1733                                value => ''};            !!!cp (78);
1734              !!!parse-error (type => 'bad attribute name');
1735            } else {
1736              !!!cp (82);
1737            }
1738            $self->{ca}
1739                = {name => chr ($self->{nc}),
1740                   value => '',
1741                   line => $self->{line}, column => $self->{column}};
1742          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1743          !!!next-input-character;          !!!next-input-character;
1744          redo A;                  redo A;        
1745        }        }
1746      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1747        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP        
1748          !!!cp (83);          !!!cp (83);
1749          ## Stay in the state          ## Stay in the state
1750          !!!next-input-character;          !!!next-input-character;
1751          redo A;          redo A;
1752        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1753          !!!cp (84);          !!!cp (84);
1754          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1755          !!!next-input-character;          !!!next-input-character;
1756          redo A;          redo A;
1757        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1758          !!!cp (85);          !!!cp (85);
1759          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1760          ## reconsume          ## reconsume
1761          redo A;          redo A;
1762        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1763          !!!cp (86);          !!!cp (86);
1764          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1765          !!!next-input-character;          !!!next-input-character;
1766          redo A;          redo A;
1767        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1768          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1769            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1770            !!!cp (87);            !!!cp (87);
1771            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1772                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1773            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1774            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1775              !!!cp (88);              !!!cp (88);
1776              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1777            } else {            } else {
1778                ## NOTE: This state should never be reached.
1779              !!!cp (89);              !!!cp (89);
1780            }            }
1781          } else {          } else {
1782            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1783          }          }
1784          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1785          !!!next-input-character;          !!!next-input-character;
1786    
1787          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1788    
1789          redo A;          redo A;
1790        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1791          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1792          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1793            !!!cp (90);            !!!cp (90);
1794            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1795                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1796            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1797            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1798              !!!cp (91);              !!!cp (91);
1799              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1800            } else {            } else {
1801                ## NOTE: This state should never be reached.
1802              !!!cp (92);              !!!cp (92);
1803            }            }
1804          } else {          } else {
1805            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1806          }          }
1807          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1808          ## reconsume          ## reconsume
1809    
1810          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1811    
1812          redo A;          redo A;
1813        } else {        } else {
1814          if ($self->{next_char} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1815            !!!cp (93);            !!!cp (93);
1816            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1817          } else {          } else {
1818            !!!cp (94);            !!!cp (94);
1819          }          }
1820          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1821          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1822          !!!next-input-character;          !!!next-input-character;
1823          redo A;          redo A;
1824        }        }
1825      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1826        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1827          !!!cp (95);          !!!cp (95);
1828          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1829          !!!next-input-character;          !!!next-input-character;
1830          redo A;          redo A;
1831        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1832          !!!cp (96);          !!!cp (96);
1833          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1834          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1835            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1836            ## implementation of the "consume a character reference" algorithm.
1837            $self->{prev_state} = $self->{state};
1838            $self->{entity_add} = 0x0022; # "
1839            $self->{state} = ENTITY_STATE;
1840          !!!next-input-character;          !!!next-input-character;
1841          redo A;          redo A;
1842        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1843          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1844          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1845            !!!cp (97);            !!!cp (97);
1846            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1847                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1848            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1849            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1850              !!!cp (98);              !!!cp (98);
1851              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1852            } else {            } else {
1853                ## NOTE: This state should never be reached.
1854              !!!cp (99);              !!!cp (99);
1855            }            }
1856          } else {          } else {
1857            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1858          }          }
1859          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1860          ## reconsume          ## reconsume
1861    
1862          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1863    
1864          redo A;          redo A;
1865        } else {        } else {
1866          !!!cp (100);          !!!cp (100);
1867          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1868            $self->{read_until}->($self->{ca}->{value},
1869                                  q["&],
1870                                  length $self->{ca}->{value});
1871    
1872          ## Stay in the state          ## Stay in the state
1873          !!!next-input-character;          !!!next-input-character;
1874          redo A;          redo A;
1875        }        }
1876      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1877        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1878          !!!cp (101);          !!!cp (101);
1879          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1880          !!!next-input-character;          !!!next-input-character;
1881          redo A;          redo A;
1882        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1883          !!!cp (102);          !!!cp (102);
1884          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1885          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1886            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1887            ## implementation of the "consume a character reference" algorithm.
1888            $self->{entity_add} = 0x0027; # '
1889            $self->{prev_state} = $self->{state};
1890            $self->{state} = ENTITY_STATE;
1891          !!!next-input-character;          !!!next-input-character;
1892          redo A;          redo A;
1893        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1894          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1895          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1896            !!!cp (103);            !!!cp (103);
1897            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1898                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1899            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1900            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1901              !!!cp (104);              !!!cp (104);
1902              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1903            } else {            } else {
1904                ## NOTE: This state should never be reached.
1905              !!!cp (105);              !!!cp (105);
1906            }            }
1907          } else {          } else {
1908            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1909          }          }
1910          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1911          ## reconsume          ## reconsume
1912    
1913          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1914    
1915          redo A;          redo A;
1916        } else {        } else {
1917          !!!cp (106);          !!!cp (106);
1918          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
1919            $self->{read_until}->($self->{ca}->{value},
1920                                  q['&],
1921                                  length $self->{ca}->{value});
1922    
1923          ## Stay in the state          ## Stay in the state
1924          !!!next-input-character;          !!!next-input-character;
1925          redo A;          redo A;
1926        }        }
1927      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1928        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # HT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
1929          !!!cp (107);          !!!cp (107);
1930          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1931          !!!next-input-character;          !!!next-input-character;
1932          redo A;          redo A;
1933        } elsif ($self->{next_char} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1934          !!!cp (108);          !!!cp (108);
1935          $self->{last_attribute_value_state} = $self->{state};          ## NOTE: In the spec, the tokenizer is switched to the
1936          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## "entity in attribute value state".  In this implementation, the
1937            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1938            ## implementation of the "consume a character reference" algorithm.
1939            $self->{entity_add} = -1;
1940            $self->{prev_state} = $self->{state};
1941            $self->{state} = ENTITY_STATE;
1942          !!!next-input-character;          !!!next-input-character;
1943          redo A;          redo A;
1944        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1945          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1946            !!!cp (109);            !!!cp (109);
1947            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1948                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1949            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1950            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1951              !!!cp (110);              !!!cp (110);
1952              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1953            } else {            } else {
1954                ## NOTE: This state should never be reached.
1955              !!!cp (111);              !!!cp (111);
1956            }            }
1957          } else {          } else {
1958            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1959          }          }
1960          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1961          !!!next-input-character;          !!!next-input-character;
1962    
1963          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1964    
1965          redo A;          redo A;
1966        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
1967          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1968          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1969            !!!cp (112);            !!!cp (112);
1970            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
1971                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1972            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1973            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1974              !!!cp (113);              !!!cp (113);
1975              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1976            } else {            } else {
1977                ## NOTE: This state should never be reached.
1978              !!!cp (114);              !!!cp (114);
1979            }            }
1980          } else {          } else {
1981            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1982          }          }
1983          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1984          ## reconsume          ## reconsume
1985    
1986          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1987    
1988          redo A;          redo A;
1989        } else {        } else {
# Line 1268  sub _get_next_token ($) { Line 1991  sub _get_next_token ($) {
1991               0x0022 => 1, # "               0x0022 => 1, # "
1992               0x0027 => 1, # '               0x0027 => 1, # '
1993               0x003D => 1, # =               0x003D => 1, # =
1994              }->{$self->{next_char}}) {              }->{$self->{nc}}) {
1995            !!!cp (115);            !!!cp (115);
1996            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1997          } else {          } else {
1998            !!!cp (116);            !!!cp (116);
1999          }          }
2000          $self->{current_attribute}->{value} .= chr ($self->{next_char});          $self->{ca}->{value} .= chr ($self->{nc});
2001            $self->{read_until}->($self->{ca}->{value},
2002                                  q["'=& >],
2003                                  length $self->{ca}->{value});
2004    
2005          ## Stay in the state          ## Stay in the state
2006          !!!next-input-character;          !!!next-input-character;
2007          redo A;          redo A;
2008        }        }
     } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {  
       my $token = $self->_tokenize_attempt_to_consume_an_entity  
           (1,  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "  
            $self->{last_attribute_value_state}  
              == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '  
            -1);  
   
       unless (defined $token) {  
         !!!cp (117);  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         !!!cp (118);  
         $self->{current_attribute}->{value} .= $token->{data};  
         $self->{current_attribute}->{has_reference} = $token->{has_reference};  
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
       }  
   
       $self->{state} = $self->{last_attribute_value_state};  
       # next-input-character is already done  
       redo A;  
2009      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2010        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2011          !!!cp (118);          !!!cp (118);
2012          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2013          !!!next-input-character;          !!!next-input-character;
2014          redo A;          redo A;
2015        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2016          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2017            !!!cp (119);            !!!cp (119);
2018            $self->{current_token}->{first_start_tag}            $self->{last_stag_name} = $self->{ct}->{tag_name};
2019                = not defined $self->{last_emitted_start_tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
2020            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2021            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2022              !!!cp (120);              !!!cp (120);
2023              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2024            } else {            } else {
2025                ## NOTE: This state should never be reached.
2026              !!!cp (121);              !!!cp (121);
2027            }            }
2028          } else {          } else {
2029            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2030          }          }
2031          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2032          !!!next-input-character;          !!!next-input-character;
2033    
2034          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2035    
2036          redo A;          redo A;
2037        } elsif ($self->{next_char} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2038            !!!cp (122);
2039            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2040          !!!next-input-character;          !!!next-input-character;
2041          if ($self->{next_char} == 0x003E and # >          redo A;
2042              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2043              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2044            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2045            !!!cp (122);            !!!cp (122.3);
2046            #            $self->{last_stag_name} = $self->{ct}->{tag_name};
2047            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2048              if ($self->{ct}->{attributes}) {
2049                !!!cp (122.1);
2050                !!!parse-error (type => 'end tag attribute');
2051              } else {
2052                ## NOTE: This state should never be reached.
2053                !!!cp (122.2);
2054              }
2055          } else {          } else {
2056            !!!cp (123);            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!parse-error (type => 'nestc');  
2057          }          }
2058          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2059          # next-input-character is already done          ## Reconsume.
2060            !!!emit ($self->{ct}); # start tag or end tag
2061          redo A;          redo A;
2062        } else {        } else {
2063          !!!cp (124);          !!!cp ('124.1');
2064          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2065          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2066          ## reconsume          ## reconsume
2067          redo A;          redo A;
2068        }        }
2069      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2070        ## (only happen if PCDATA state)        if ($self->{nc} == 0x003E) { # >
2071                  if ($self->{ct}->{type} == END_TAG_TOKEN) {
2072        my $token = {type => COMMENT_TOKEN, data => ''};            !!!cp ('124.2');
2073              !!!parse-error (type => 'nestc', token => $self->{ct});
2074        BC: {            ## TODO: Different type than slash in start tag
2075          if ($self->{next_char} == 0x003E) { # >            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2076            !!!cp (124);            if ($self->{ct}->{attributes}) {
2077            $self->{state} = DATA_STATE;              !!!cp ('124.4');
2078            !!!next-input-character;              !!!parse-error (type => 'end tag attribute');
2079              } else {
2080            !!!emit ($token);              !!!cp ('124.5');
2081              }
2082              ## TODO: Test |<title></title/>|
2083            } else {
2084              !!!cp ('124.3');
2085              $self->{self_closing} = 1;
2086            }
2087    
2088            redo A;          $self->{state} = DATA_STATE;
2089          } elsif ($self->{next_char} == -1) {          !!!next-input-character;
           !!!cp (125);  
           $self->{state} = DATA_STATE;  
           ## reconsume  
2090    
2091            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2092    
2093            redo A;          redo A;
2094          } elsif ($self->{nc} == -1) {
2095            !!!parse-error (type => 'unclosed tag');
2096            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2097              !!!cp (124.7);
2098              $self->{last_stag_name} = $self->{ct}->{tag_name};
2099            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2100              if ($self->{ct}->{attributes}) {
2101                !!!cp (124.5);
2102                !!!parse-error (type => 'end tag attribute');
2103              } else {
2104                ## NOTE: This state should never be reached.
2105                !!!cp (124.6);
2106              }
2107          } else {          } else {
2108            !!!cp (126);            die "$0: $self->{ct}->{type}: Unknown token type";
           $token->{data} .= chr ($self->{next_char});  
           !!!next-input-character;  
           redo BC;  
2109          }          }
2110        } # BC          $self->{state} = DATA_STATE;
2111            ## Reconsume.
2112        die "$0: _get_next_token: unexpected case [BC]";          !!!emit ($self->{ct}); # start tag or end tag
2113      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {          redo A;
2114          } else {
2115            !!!cp ('124.4');
2116            !!!parse-error (type => 'nestc');
2117            ## TODO: This error type is wrong.
2118            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2119            ## Reconsume.
2120            redo A;
2121          }
2122        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2123        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2124    
2125        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2126        push @next_char, $self->{next_char};        ## consumes characters one-by-one basis.
2127                
2128        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2129            !!!cp (124);
2130            $self->{state} = DATA_STATE;
2131          !!!next-input-character;          !!!next-input-character;
2132          push @next_char, $self->{next_char};  
2133          if ($self->{next_char} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2134            !!!cp (127);          redo A;
2135            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};        } elsif ($self->{nc} == -1) {
2136            $self->{state} = COMMENT_START_STATE;          !!!cp (125);
2137            !!!next-input-character;          $self->{state} = DATA_STATE;
2138            redo A;          ## reconsume
2139          } else {  
2140            !!!cp (128);          !!!emit ($self->{ct}); # comment
2141          }          redo A;
2142        } elsif ($self->{next_char} == 0x0044 or # D        } else {
2143                 $self->{next_char} == 0x0064) { # d          !!!cp (126);
2144            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2145            $self->{read_until}->($self->{ct}->{data},
2146                                  q[>],
2147                                  length $self->{ct}->{data});
2148    
2149            ## Stay in the state.
2150          !!!next-input-character;          !!!next-input-character;
2151          push @next_char, $self->{next_char};          redo A;
2152          if ($self->{next_char} == 0x004F or # O        }
2153              $self->{next_char} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2154            !!!next-input-character;        ## (only happen if PCDATA state)
2155            push @next_char, $self->{next_char};        
2156            if ($self->{next_char} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2157                $self->{next_char} == 0x0063) { # c          !!!cp (133);
2158              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2159              push @next_char, $self->{next_char};          !!!next-input-character;
2160              if ($self->{next_char} == 0x0054 or # T          redo A;
2161                  $self->{next_char} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2162                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2163                push @next_char, $self->{next_char};          ## ASCII case-insensitive.
2164                if ($self->{next_char} == 0x0059 or # Y          !!!cp (130);
2165                    $self->{next_char} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2166                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2167                  push @next_char, $self->{next_char};          !!!next-input-character;
2168                  if ($self->{next_char} == 0x0050 or # P          redo A;
2169                      $self->{next_char} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2170                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2171                    push @next_char, $self->{next_char};                 $self->{nc} == 0x005B) { # [
2172                    if ($self->{next_char} == 0x0045 or # E          !!!cp (135.4);                
2173                        $self->{next_char} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2174                      !!!cp (129);          $self->{s_kwd} = '[';
2175                      ## TODO: What a stupid code this is!          !!!next-input-character;
2176                      $self->{state} = DOCTYPE_STATE;          redo A;
                     !!!next-input-character;  
                     redo A;  
                   } else {  
                     !!!cp (130);  
                   }  
                 } else {  
                   !!!cp (131);  
                 }  
               } else {  
                 !!!cp (132);  
               }  
             } else {  
               !!!cp (133);  
             }  
           } else {  
             !!!cp (134);  
           }  
         } else {  
           !!!cp (135);  
         }  
2177        } else {        } else {
2178          !!!cp (136);          !!!cp (136);
2179        }        }
2180    
2181        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2182        $self->{next_char} = shift @next_char;                        line => $self->{line_prev},
2183        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2184          ## Reconsume.
2185        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2186          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2187                                    line => $self->{line_prev},
2188                                    column => $self->{column_prev} - 1,
2189                                   };
2190        redo A;        redo A;
2191              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2192        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2193        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2194            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2195                                      line => $self->{line_prev},
2196                                      column => $self->{column_prev} - 2,
2197                                     };
2198            $self->{state} = COMMENT_START_STATE;
2199            !!!next-input-character;
2200            redo A;
2201          } else {
2202            !!!cp (128);
2203            !!!parse-error (type => 'bogus comment',
2204                            line => $self->{line_prev},
2205                            column => $self->{column_prev} - 2);
2206            $self->{state} = BOGUS_COMMENT_STATE;
2207            ## Reconsume.
2208            $self->{ct} = {type => COMMENT_TOKEN,
2209                                      data => '-',
2210                                      line => $self->{line_prev},
2211                                      column => $self->{column_prev} - 2,
2212                                     };
2213            redo A;
2214          }
2215        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2216          ## ASCII case-insensitive.
2217          if ($self->{nc} == [
2218                undef,
2219                0x004F, # O
2220                0x0043, # C
2221                0x0054, # T
2222                0x0059, # Y
2223                0x0050, # P
2224              ]->[length $self->{s_kwd}] or
2225              $self->{nc} == [
2226                undef,
2227                0x006F, # o
2228                0x0063, # c
2229                0x0074, # t
2230                0x0079, # y
2231                0x0070, # p
2232              ]->[length $self->{s_kwd}]) {
2233            !!!cp (131);
2234            ## Stay in the state.
2235            $self->{s_kwd} .= chr $self->{nc};
2236            !!!next-input-character;
2237            redo A;
2238          } elsif ((length $self->{s_kwd}) == 6 and
2239                   ($self->{nc} == 0x0045 or # E
2240                    $self->{nc} == 0x0065)) { # e
2241            !!!cp (129);
2242            $self->{state} = DOCTYPE_STATE;
2243            $self->{ct} = {type => DOCTYPE_TOKEN,
2244                                      quirks => 1,
2245                                      line => $self->{line_prev},
2246                                      column => $self->{column_prev} - 7,
2247                                     };
2248            !!!next-input-character;
2249            redo A;
2250          } else {
2251            !!!cp (132);        
2252            !!!parse-error (type => 'bogus comment',
2253                            line => $self->{line_prev},
2254                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2255            $self->{state} = BOGUS_COMMENT_STATE;
2256            ## Reconsume.
2257            $self->{ct} = {type => COMMENT_TOKEN,
2258                                      data => $self->{s_kwd},
2259                                      line => $self->{line_prev},
2260                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2261                                     };
2262            redo A;
2263          }
2264        } elsif ($self->{state} == MD_CDATA_STATE) {
2265          if ($self->{nc} == {
2266                '[' => 0x0043, # C
2267                '[C' => 0x0044, # D
2268                '[CD' => 0x0041, # A
2269                '[CDA' => 0x0054, # T
2270                '[CDAT' => 0x0041, # A
2271              }->{$self->{s_kwd}}) {
2272            !!!cp (135.1);
2273            ## Stay in the state.
2274            $self->{s_kwd} .= chr $self->{nc};
2275            !!!next-input-character;
2276            redo A;
2277          } elsif ($self->{s_kwd} eq '[CDATA' and
2278                   $self->{nc} == 0x005B) { # [
2279            !!!cp (135.2);
2280            $self->{ct} = {type => CHARACTER_TOKEN,
2281                                      data => '',
2282                                      line => $self->{line_prev},
2283                                      column => $self->{column_prev} - 7};
2284            $self->{state} = CDATA_SECTION_STATE;
2285            !!!next-input-character;
2286            redo A;
2287          } else {
2288            !!!cp (135.3);
2289            !!!parse-error (type => 'bogus comment',
2290                            line => $self->{line_prev},
2291                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2292            $self->{state} = BOGUS_COMMENT_STATE;
2293            ## Reconsume.
2294            $self->{ct} = {type => COMMENT_TOKEN,
2295                                      data => $self->{s_kwd},
2296                                      line => $self->{line_prev},
2297                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2298                                     };
2299            redo A;
2300          }
2301      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2302        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2303          !!!cp (137);          !!!cp (137);
2304          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2305          !!!next-input-character;          !!!next-input-character;
2306          redo A;          redo A;
2307        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2308          !!!cp (138);          !!!cp (138);
2309          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2310          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2311          !!!next-input-character;          !!!next-input-character;
2312    
2313          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2314    
2315          redo A;          redo A;
2316        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2317          !!!cp (139);          !!!cp (139);
2318          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2319          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2320          ## reconsume          ## reconsume
2321    
2322          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2323    
2324          redo A;          redo A;
2325        } else {        } else {
2326          !!!cp (140);          !!!cp (140);
2327          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2328              .= chr ($self->{next_char});              .= chr ($self->{nc});
2329          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2330          !!!next-input-character;          !!!next-input-character;
2331          redo A;          redo A;
2332        }        }
2333      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2334        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2335          !!!cp (141);          !!!cp (141);
2336          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2337          !!!next-input-character;          !!!next-input-character;
2338          redo A;          redo A;
2339        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2340          !!!cp (142);          !!!cp (142);
2341          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2342          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2343          !!!next-input-character;          !!!next-input-character;
2344    
2345          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2346    
2347          redo A;          redo A;
2348        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2349          !!!cp (143);          !!!cp (143);
2350          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2351          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2352          ## reconsume          ## reconsume
2353    
2354          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2355    
2356          redo A;          redo A;
2357        } else {        } else {
2358          !!!cp (144);          !!!cp (144);
2359          $self->{current_token}->{data} # comment          $self->{ct}->{data} # comment
2360              .= '-' . chr ($self->{next_char});              .= '-' . chr ($self->{nc});
2361          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2362          !!!next-input-character;          !!!next-input-character;
2363          redo A;          redo A;
2364        }        }
2365      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2366        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2367          !!!cp (145);          !!!cp (145);
2368          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2369          !!!next-input-character;          !!!next-input-character;
2370          redo A;          redo A;
2371        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2372          !!!cp (146);          !!!cp (146);
2373          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2374          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2375          ## reconsume          ## reconsume
2376    
2377          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2378    
2379          redo A;          redo A;
2380        } else {        } else {
2381          !!!cp (147);          !!!cp (147);
2382          $self->{current_token}->{data} .= chr ($self->{next_char}); # comment          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2383            $self->{read_until}->($self->{ct}->{data},
2384                                  q[-],
2385                                  length $self->{ct}->{data});
2386    
2387          ## Stay in the state          ## Stay in the state
2388          !!!next-input-character;          !!!next-input-character;
2389          redo A;          redo A;
2390        }        }
2391      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2392        if ($self->{next_char} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2393          !!!cp (148);          !!!cp (148);
2394          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2395          !!!next-input-character;          !!!next-input-character;
2396          redo A;          redo A;
2397        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2398          !!!cp (149);          !!!cp (149);
2399          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2400          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2401          ## reconsume          ## reconsume
2402    
2403          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2404    
2405          redo A;          redo A;
2406        } else {        } else {
2407          !!!cp (150);          !!!cp (150);
2408          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2409          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2410          !!!next-input-character;          !!!next-input-character;
2411          redo A;          redo A;
2412        }        }
2413      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2414        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2415          !!!cp (151);          !!!cp (151);
2416          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2417          !!!next-input-character;          !!!next-input-character;
2418    
2419          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2420    
2421          redo A;          redo A;
2422        } elsif ($self->{next_char} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2423          !!!cp (152);          !!!cp (152);
2424          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2425          $self->{current_token}->{data} .= '-'; # comment                          line => $self->{line_prev},
2426                            column => $self->{column_prev});
2427            $self->{ct}->{data} .= '-'; # comment
2428          ## Stay in the state          ## Stay in the state
2429          !!!next-input-character;          !!!next-input-character;
2430          redo A;          redo A;
2431        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2432          !!!cp (153);          !!!cp (153);
2433          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2434          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2435          ## reconsume          ## reconsume
2436    
2437          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2438    
2439          redo A;          redo A;
2440        } else {        } else {
2441          !!!cp (154);          !!!cp (154);
2442          !!!parse-error (type => 'dash in comment');          !!!parse-error (type => 'dash in comment',
2443          $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment                          line => $self->{line_prev},
2444                            column => $self->{column_prev});
2445            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2446          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2447          !!!next-input-character;          !!!next-input-character;
2448          redo A;          redo A;
2449        }        }
2450      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2451        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2452          !!!cp (155);          !!!cp (155);
2453          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2454          !!!next-input-character;          !!!next-input-character;
# Line 1625  sub _get_next_token ($) { Line 2461  sub _get_next_token ($) {
2461          redo A;          redo A;
2462        }        }
2463      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2464        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2465          !!!cp (157);          !!!cp (157);
2466          ## Stay in the state          ## Stay in the state
2467          !!!next-input-character;          !!!next-input-character;
2468          redo A;          redo A;
2469        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2470          !!!cp (158);          !!!cp (158);
2471          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2472          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2473          !!!next-input-character;          !!!next-input-character;
2474    
2475          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2476    
2477          redo A;          redo A;
2478        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2479          !!!cp (159);          !!!cp (159);
2480          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2481          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2482          ## reconsume          ## reconsume
2483    
2484          !!!emit ({type => DOCTYPE_TOKEN, quirks => 1});          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2485    
2486          redo A;          redo A;
2487        } else {        } else {
2488          !!!cp (160);          !!!cp (160);
2489          $self->{current_token}          $self->{ct}->{name} = chr $self->{nc};
2490              = {type => DOCTYPE_TOKEN,          delete $self->{ct}->{quirks};
                name => chr ($self->{next_char}),  
                #quirks => 0,  
               };  
 ## ISSUE: "Set the token's name name to the" in the spec  
2491          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2492          !!!next-input-character;          !!!next-input-character;
2493          redo A;          redo A;
2494        }        }
2495      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2496  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2497        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2498          !!!cp (161);          !!!cp (161);
2499          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2500          !!!next-input-character;          !!!next-input-character;
2501          redo A;          redo A;
2502        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2503          !!!cp (162);          !!!cp (162);
2504          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2505          !!!next-input-character;          !!!next-input-character;
2506    
2507          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2508    
2509          redo A;          redo A;
2510        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2511          !!!cp (163);          !!!cp (163);
2512          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2513          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2514          ## reconsume          ## reconsume
2515    
2516          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2517          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2518    
2519          redo A;          redo A;
2520        } else {        } else {
2521          !!!cp (164);          !!!cp (164);
2522          $self->{current_token}->{name}          $self->{ct}->{name}
2523            .= chr ($self->{next_char}); # DOCTYPE            .= chr ($self->{nc}); # DOCTYPE
2524          ## Stay in the state          ## Stay in the state
2525          !!!next-input-character;          !!!next-input-character;
2526          redo A;          redo A;
2527        }        }
2528      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2529        if ($self->{next_char} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
           $self->{next_char} == 0x000A or # LF  
           $self->{next_char} == 0x000B or # VT  
           $self->{next_char} == 0x000C or # FF  
           $self->{next_char} == 0x0020) { # SP  
2530          !!!cp (165);          !!!cp (165);
2531          ## Stay in the state          ## Stay in the state
2532          !!!next-input-character;          !!!next-input-character;
2533          redo A;          redo A;
2534        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2535          !!!cp (166);          !!!cp (166);
2536          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2537          !!!next-input-character;          !!!next-input-character;
2538    
2539          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2540    
2541          redo A;          redo A;
2542        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2543          !!!cp (167);          !!!cp (167);
2544          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2545          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2546          ## reconsume          ## reconsume
2547    
2548          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2549          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2550    
2551          redo A;          redo A;
2552        } elsif ($self->{next_char} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2553                 $self->{next_char} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2554            $self->{state} = PUBLIC_STATE;
2555            $self->{s_kwd} = chr $self->{nc};
2556          !!!next-input-character;          !!!next-input-character;
2557          if ($self->{next_char} == 0x0055 or # U          redo A;
2558              $self->{next_char} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2559            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2560            if ($self->{next_char} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2561                $self->{next_char} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_char} == 0x004C or # L  
                 $self->{next_char} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0049 or # I  
                   $self->{next_char} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x0043 or # C  
                     $self->{next_char} == 0x0063) { # c  
                   !!!cp (168);  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (169);  
                 }  
               } else {  
                 !!!cp (170);  
               }  
             } else {  
               !!!cp (171);  
             }  
           } else {  
             !!!cp (172);  
           }  
         } else {  
           !!!cp (173);  
         }  
   
         #  
       } elsif ($self->{next_char} == 0x0053 or # S  
                $self->{next_char} == 0x0073) { # s  
2562          !!!next-input-character;          !!!next-input-character;
2563          if ($self->{next_char} == 0x0059 or # Y          redo A;
             $self->{next_char} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_char} == 0x0053 or # S  
               $self->{next_char} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_char} == 0x0054 or # T  
                 $self->{next_char} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_char} == 0x0045 or # E  
                   $self->{next_char} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_char} == 0x004D or # M  
                     $self->{next_char} == 0x006D) { # m  
                   !!!cp (174);  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 } else {  
                   !!!cp (175);  
                 }  
               } else {  
                 !!!cp (176);  
               }  
             } else {  
               !!!cp (177);  
             }  
           } else {  
             !!!cp (178);  
           }  
         } else {  
           !!!cp (179);  
         }  
   
         #  
2564        } else {        } else {
2565          !!!cp (180);          !!!cp (180);
2566            !!!parse-error (type => 'string after DOCTYPE name');
2567            $self->{ct}->{quirks} = 1;
2568    
2569            $self->{state} = BOGUS_DOCTYPE_STATE;
2570          !!!next-input-character;          !!!next-input-character;
2571          #          redo A;
2572        }        }
2573        } elsif ($self->{state} == PUBLIC_STATE) {
2574          ## ASCII case-insensitive
2575          if ($self->{nc} == [
2576                undef,
2577                0x0055, # U
2578                0x0042, # B
2579                0x004C, # L
2580                0x0049, # I
2581              ]->[length $self->{s_kwd}] or
2582              $self->{nc} == [
2583                undef,
2584                0x0075, # u
2585                0x0062, # b
2586                0x006C, # l
2587                0x0069, # i
2588              ]->[length $self->{s_kwd}]) {
2589            !!!cp (175);
2590            ## Stay in the state.
2591            $self->{s_kwd} .= chr $self->{nc};
2592            !!!next-input-character;
2593            redo A;
2594          } elsif ((length $self->{s_kwd}) == 5 and
2595                   ($self->{nc} == 0x0043 or # C
2596                    $self->{nc} == 0x0063)) { # c
2597            !!!cp (168);
2598            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2599            !!!next-input-character;
2600            redo A;
2601          } else {
2602            !!!cp (169);
2603            !!!parse-error (type => 'string after DOCTYPE name',
2604                            line => $self->{line_prev},
2605                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2606            $self->{ct}->{quirks} = 1;
2607    
2608        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2609        $self->{current_token}->{quirks} = 1;          ## Reconsume.
2610            redo A;
2611          }
2612        } elsif ($self->{state} == SYSTEM_STATE) {
2613          ## ASCII case-insensitive
2614          if ($self->{nc} == [
2615                undef,
2616                0x0059, # Y
2617                0x0053, # S
2618                0x0054, # T
2619                0x0045, # E
2620              ]->[length $self->{s_kwd}] or
2621              $self->{nc} == [
2622                undef,
2623                0x0079, # y
2624                0x0073, # s
2625                0x0074, # t
2626                0x0065, # e
2627              ]->[length $self->{s_kwd}]) {
2628            !!!cp (170);
2629            ## Stay in the state.
2630            $self->{s_kwd} .= chr $self->{nc};
2631            !!!next-input-character;
2632            redo A;
2633          } elsif ((length $self->{s_kwd}) == 5 and
2634                   ($self->{nc} == 0x004D or # M
2635                    $self->{nc} == 0x006D)) { # m
2636            !!!cp (171);
2637            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2638            !!!next-input-character;
2639            redo A;
2640          } else {
2641            !!!cp (172);
2642            !!!parse-error (type => 'string after DOCTYPE name',
2643                            line => $self->{line_prev},
2644                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2645            $self->{ct}->{quirks} = 1;
2646    
2647        $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2648        # next-input-character is already done          ## Reconsume.
2649        redo A;          redo A;
2650          }
2651      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2652        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2653          !!!cp (181);          !!!cp (181);
2654          ## Stay in the state          ## Stay in the state
2655          !!!next-input-character;          !!!next-input-character;
2656          redo A;          redo A;
2657        } elsif ($self->{next_char} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2658          !!!cp (182);          !!!cp (182);
2659          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2660          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2661          !!!next-input-character;          !!!next-input-character;
2662          redo A;          redo A;
2663        } elsif ($self->{next_char} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2664          !!!cp (183);          !!!cp (183);
2665          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{ct}->{pubid} = ''; # DOCTYPE
2666          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2667          !!!next-input-character;          !!!next-input-character;
2668          redo A;          redo A;
2669        } elsif ($self->{next_char} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2670          !!!cp (184);          !!!cp (184);
2671          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2672    
2673          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2674          !!!next-input-character;          !!!next-input-character;
2675    
2676          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2677          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2678    
2679          redo A;          redo A;
2680        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2681          !!!cp (185);          !!!cp (185);
2682          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2683    
2684          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2685          ## reconsume          ## reconsume
2686    
2687          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2688          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2689    
2690          redo A;          redo A;
2691        } else {        } else {
2692          !!!cp (186);          !!!cp (186);
2693          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2694          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2695    
2696          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2697          !!!next-input-character;          !!!next-input-character;
2698          redo A;          redo A;
2699        }        }
2700      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2701        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2702          !!!cp (187);          !!!cp (187);
2703          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2704          !!!next-input-character;          !!!next-input-character;
2705          redo A;          redo A;
2706        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2707          !!!cp (188);          !!!cp (188);
2708          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2709    
2710          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2711          !!!next-input-character;          !!!next-input-character;
2712    
2713          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2714          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2715    
2716          redo A;          redo A;
2717        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2718          !!!cp (189);          !!!cp (189);
2719          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2720    
2721          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2722          ## reconsume          ## reconsume
2723    
2724          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2725          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2726    
2727          redo A;          redo A;
2728        } else {        } else {
2729          !!!cp (190);          !!!cp (190);
2730          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2731              .= chr $self->{next_char};              .= chr $self->{nc};
2732            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2733                                  length $self->{ct}->{pubid});
2734    
2735          ## Stay in the state          ## Stay in the state
2736          !!!next-input-character;          !!!next-input-character;
2737          redo A;          redo A;
2738        }        }
2739      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2740        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2741          !!!cp (191);          !!!cp (191);
2742          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2743          !!!next-input-character;          !!!next-input-character;
2744          redo A;          redo A;
2745        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2746          !!!cp (192);          !!!cp (192);
2747          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2748    
2749          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2750          !!!next-input-character;          !!!next-input-character;
2751    
2752          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2753          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2754    
2755          redo A;          redo A;
2756        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2757          !!!cp (193);          !!!cp (193);
2758          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2759    
2760          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2761          ## reconsume          ## reconsume
2762    
2763          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2764          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2765    
2766          redo A;          redo A;
2767        } else {        } else {
2768          !!!cp (194);          !!!cp (194);
2769          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2770              .= chr $self->{next_char};              .= chr $self->{nc};
2771            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2772                                  length $self->{ct}->{pubid});
2773    
2774          ## Stay in the state          ## Stay in the state
2775          !!!next-input-character;          !!!next-input-character;
2776          redo A;          redo A;
2777        }        }
2778      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2779        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2780          !!!cp (195);          !!!cp (195);
2781          ## Stay in the state          ## Stay in the state
2782          !!!next-input-character;          !!!next-input-character;
2783          redo A;          redo A;
2784        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2785          !!!cp (196);          !!!cp (196);
2786          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2787          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2788          !!!next-input-character;          !!!next-input-character;
2789          redo A;          redo A;
2790        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2791          !!!cp (197);          !!!cp (197);
2792          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2793          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2794          !!!next-input-character;          !!!next-input-character;
2795          redo A;          redo A;
2796        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2797          !!!cp (198);          !!!cp (198);
2798          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2799          !!!next-input-character;          !!!next-input-character;
2800    
2801          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2802    
2803          redo A;          redo A;
2804        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2805          !!!cp (199);          !!!cp (199);
2806          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2807    
2808          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2809          ## reconsume          ## reconsume
2810    
2811          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2812          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2813    
2814          redo A;          redo A;
2815        } else {        } else {
2816          !!!cp (200);          !!!cp (200);
2817          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2818          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2819    
2820          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2821          !!!next-input-character;          !!!next-input-character;
2822          redo A;          redo A;
2823        }        }
2824      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2825        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2826          !!!cp (201);          !!!cp (201);
2827          ## Stay in the state          ## Stay in the state
2828          !!!next-input-character;          !!!next-input-character;
2829          redo A;          redo A;
2830        } elsif ($self->{next_char} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2831          !!!cp (202);          !!!cp (202);
2832          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2833          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2834          !!!next-input-character;          !!!next-input-character;
2835          redo A;          redo A;
2836        } elsif ($self->{next_char} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2837          !!!cp (203);          !!!cp (203);
2838          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{ct}->{sysid} = ''; # DOCTYPE
2839          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2840          !!!next-input-character;          !!!next-input-character;
2841          redo A;          redo A;
2842        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2843          !!!cp (204);          !!!cp (204);
2844          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2845          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2846          !!!next-input-character;          !!!next-input-character;
2847    
2848          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2849          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2850    
2851          redo A;          redo A;
2852        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2853          !!!cp (205);          !!!cp (205);
2854          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2855    
2856          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2857          ## reconsume          ## reconsume
2858    
2859          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2860          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2861    
2862          redo A;          redo A;
2863        } else {        } else {
2864          !!!cp (206);          !!!cp (206);
2865          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2866          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2867    
2868          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2869          !!!next-input-character;          !!!next-input-character;
2870          redo A;          redo A;
2871        }        }
2872      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2873        if ($self->{next_char} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2874          !!!cp (207);          !!!cp (207);
2875          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2876          !!!next-input-character;          !!!next-input-character;
2877          redo A;          redo A;
2878        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2879          !!!cp (208);          !!!cp (208);
2880          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2881    
2882          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2883          !!!next-input-character;          !!!next-input-character;
2884    
2885          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2886          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2887    
2888          redo A;          redo A;
2889        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2890          !!!cp (209);          !!!cp (209);
2891          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2892    
2893          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2894          ## reconsume          ## reconsume
2895    
2896          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2897          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2898    
2899          redo A;          redo A;
2900        } else {        } else {
2901          !!!cp (210);          !!!cp (210);
2902          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2903              .= chr $self->{next_char};              .= chr $self->{nc};
2904            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2905                                  length $self->{ct}->{sysid});
2906    
2907          ## Stay in the state          ## Stay in the state
2908          !!!next-input-character;          !!!next-input-character;
2909          redo A;          redo A;
2910        }        }
2911      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2912        if ($self->{next_char} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2913          !!!cp (211);          !!!cp (211);
2914          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2915          !!!next-input-character;          !!!next-input-character;
2916          redo A;          redo A;
2917        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2918          !!!cp (212);          !!!cp (212);
2919          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2920    
2921          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2922          !!!next-input-character;          !!!next-input-character;
2923    
2924          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2925          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2926    
2927          redo A;          redo A;
2928        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2929          !!!cp (213);          !!!cp (213);
2930          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2931    
2932          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2933          ## reconsume          ## reconsume
2934    
2935          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2936          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2937    
2938          redo A;          redo A;
2939        } else {        } else {
2940          !!!cp (214);          !!!cp (214);
2941          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2942              .= chr $self->{next_char};              .= chr $self->{nc};
2943            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2944                                  length $self->{ct}->{sysid});
2945    
2946          ## Stay in the state          ## Stay in the state
2947          !!!next-input-character;          !!!next-input-character;
2948          redo A;          redo A;
2949        }        }
2950      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2951        if ({        if ($is_space->{$self->{nc}}) {
             0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,  
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_char}}) {  
2952          !!!cp (215);          !!!cp (215);
2953          ## Stay in the state          ## Stay in the state
2954          !!!next-input-character;          !!!next-input-character;
2955          redo A;          redo A;
2956        } elsif ($self->{next_char} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2957          !!!cp (216);          !!!cp (216);
2958          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2959          !!!next-input-character;          !!!next-input-character;
2960    
2961          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2962    
2963          redo A;          redo A;
2964        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2965          !!!cp (217);          !!!cp (217);
2966          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2967          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2968          ## reconsume          ## reconsume
2969    
2970          $self->{current_token}->{quirks} = 1;          $self->{ct}->{quirks} = 1;
2971          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2972    
2973          redo A;          redo A;
2974        } else {        } else {
2975          !!!cp (218);          !!!cp (218);
2976          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2977          #$self->{current_token}->{quirks} = 1;          #$self->{ct}->{quirks} = 1;
2978    
2979          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2980          !!!next-input-character;          !!!next-input-character;
2981          redo A;          redo A;
2982        }        }
2983      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2984        if ($self->{next_char} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2985          !!!cp (219);          !!!cp (219);
2986          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2987          !!!next-input-character;          !!!next-input-character;
2988    
2989          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2990    
2991          redo A;          redo A;
2992        } elsif ($self->{next_char} == -1) {        } elsif ($self->{nc} == -1) {
2993          !!!cp (220);          !!!cp (220);
         !!!parse-error (type => 'unclosed DOCTYPE');  
2994          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2995          ## reconsume          ## reconsume
2996    
2997          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2998    
2999          redo A;          redo A;
3000        } else {        } else {
3001          !!!cp (221);          !!!cp (221);
3002            my $s = '';
3003            $self->{read_until}->($s, q[>], 0);
3004    
3005          ## Stay in the state          ## Stay in the state
3006          !!!next-input-character;          !!!next-input-character;
3007          redo A;          redo A;
3008        }        }
3009      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3010        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3011      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3012    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3013          
3014    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3015  } # _get_next_token          !!!cp (221.1);
3016            $self->{state} = CDATA_SECTION_MSE1_STATE;
3017            !!!next-input-character;
3018            redo A;
3019          } elsif ($self->{nc} == -1) {
3020            $self->{state} = DATA_STATE;
3021            !!!next-input-character;
3022            if (length $self->{ct}->{data}) { # character
3023              !!!cp (221.2);
3024              !!!emit ($self->{ct}); # character
3025            } else {
3026              !!!cp (221.3);
3027              ## No token to emit. $self->{ct} is discarded.
3028            }        
3029            redo A;
3030          } else {
3031            !!!cp (221.4);
3032            $self->{ct}->{data} .= chr $self->{nc};
3033            $self->{read_until}->($self->{ct}->{data},
3034                                  q<]>,
3035                                  length $self->{ct}->{data});
3036    
3037  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3038    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3039            redo A;
3040          }
3041    
3042    if ({        ## ISSUE: "text tokens" in spec.
3043         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3044         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3045         $additional => 1,          !!!cp (221.5);
3046        }->{$self->{next_char}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3047      ## Don't consume          !!!next-input-character;
3048      ## No error          redo A;
3049      return undef;        } else {
3050    } elsif ($self->{next_char} == 0x0023) { # #          !!!cp (221.6);
3051      !!!next-input-character;          $self->{ct}->{data} .= ']';
3052      if ($self->{next_char} == 0x0078 or # x          $self->{state} = CDATA_SECTION_STATE;
3053          $self->{next_char} == 0x0058) { # X          ## Reconsume.
3054        my $code;          redo A;
3055        X: {        }
3056          my $x_char = $self->{next_char};      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3057          !!!next-input-character;        if ($self->{nc} == 0x003E) { # >
3058          if (0x0030 <= $self->{next_char} and          $self->{state} = DATA_STATE;
3059              $self->{next_char} <= 0x0039) { # 0..9          !!!next-input-character;
3060            $code ||= 0;          if (length $self->{ct}->{data}) { # character
3061            $code *= 0x10;            !!!cp (221.7);
3062            $code += $self->{next_char} - 0x0030;            !!!emit ($self->{ct}); # character
           redo X;  
         } elsif (0x0061 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_char} and  
                  $self->{next_char} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_char} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_char});  
           $self->{next_char} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_char} == 0x003B) { # ;  
           !!!next-input-character;  
3063          } else {          } else {
3064            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3065              ## No token to emit. $self->{ct} is discarded.
3066          }          }
3067            redo A;
3068          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3069            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3070            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3071          } 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_char} and  
              $self->{next_char} <= 0x0039) { # 0..9  
       my $code = $self->{next_char} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_char} and  
                 $self->{next_char} <= 0x0039) { # 0..9  
         $code *= 10;  
         $code += $self->{next_char} - 0x0030;  
           
3072          !!!next-input-character;          !!!next-input-character;
3073            redo A;
3074          } else {
3075            !!!cp (221.11);
3076            $self->{ct}->{data} .= ']]'; # character
3077            $self->{state} = CDATA_SECTION_STATE;
3078            ## Reconsume.
3079            redo A;
3080          }
3081        } elsif ($self->{state} == ENTITY_STATE) {
3082          if ($is_space->{$self->{nc}} or
3083              {
3084                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3085                $self->{entity_add} => 1,
3086              }->{$self->{nc}}) {
3087            !!!cp (1001);
3088            ## Don't consume
3089            ## No error
3090            ## Return nothing.
3091            #
3092          } elsif ($self->{nc} == 0x0023) { # #
3093            !!!cp (999);
3094            $self->{state} = ENTITY_HASH_STATE;
3095            $self->{s_kwd} = '#';
3096            !!!next-input-character;
3097            redo A;
3098          } elsif ((0x0041 <= $self->{nc} and
3099                    $self->{nc} <= 0x005A) or # A..Z
3100                   (0x0061 <= $self->{nc} and
3101                    $self->{nc} <= 0x007A)) { # a..z
3102            !!!cp (998);
3103            require Whatpm::_NamedEntityList;
3104            $self->{state} = ENTITY_NAME_STATE;
3105            $self->{s_kwd} = chr $self->{nc};
3106            $self->{entity__value} = $self->{s_kwd};
3107            $self->{entity__match} = 0;
3108            !!!next-input-character;
3109            redo A;
3110          } else {
3111            !!!cp (1027);
3112            !!!parse-error (type => 'bare ero');
3113            ## Return nothing.
3114            #
3115        }        }
3116    
3117        if ($self->{next_char} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3118          ## reference" algorithm.  In other word, there is an "&" character
3119          ## that does not introduce a character reference, which would be
3120          ## appended to the parent element or the attribute value in later
3121          ## process of the tokenizer.
3122    
3123          if ($self->{prev_state} == DATA_STATE) {
3124            !!!cp (997);
3125            $self->{state} = $self->{prev_state};
3126            ## Reconsume.
3127            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3128                      line => $self->{line_prev},
3129                      column => $self->{column_prev},
3130                     });
3131            redo A;
3132          } else {
3133            !!!cp (996);
3134            $self->{ca}->{value} .= '&';
3135            $self->{state} = $self->{prev_state};
3136            ## Reconsume.
3137            redo A;
3138          }
3139        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3140          if ($self->{nc} == 0x0078 or # x
3141              $self->{nc} == 0x0058) { # X
3142            !!!cp (995);
3143            $self->{state} = HEXREF_X_STATE;
3144            $self->{s_kwd} .= chr $self->{nc};
3145            !!!next-input-character;
3146            redo A;
3147          } elsif (0x0030 <= $self->{nc} and
3148                   $self->{nc} <= 0x0039) { # 0..9
3149            !!!cp (994);
3150            $self->{state} = NCR_NUM_STATE;
3151            $self->{s_kwd} = $self->{nc} - 0x0030;
3152            !!!next-input-character;
3153            redo A;
3154          } else {
3155            !!!parse-error (type => 'bare nero',
3156                            line => $self->{line_prev},
3157                            column => $self->{column_prev} - 1);
3158    
3159            ## NOTE: According to the spec algorithm, nothing is returned,
3160            ## and then "&#" is appended to the parent element or the attribute
3161            ## value in the later processing.
3162    
3163            if ($self->{prev_state} == DATA_STATE) {
3164              !!!cp (1019);
3165              $self->{state} = $self->{prev_state};
3166              ## Reconsume.
3167              !!!emit ({type => CHARACTER_TOKEN,
3168                        data => '&#',
3169                        line => $self->{line_prev},
3170                        column => $self->{column_prev} - 1,
3171                       });
3172              redo A;
3173            } else {
3174              !!!cp (993);
3175              $self->{ca}->{value} .= '&#';
3176              $self->{state} = $self->{prev_state};
3177              ## Reconsume.
3178              redo A;
3179            }
3180          }
3181        } elsif ($self->{state} == NCR_NUM_STATE) {
3182          if (0x0030 <= $self->{nc} and
3183              $self->{nc} <= 0x0039) { # 0..9
3184            !!!cp (1012);
3185            $self->{s_kwd} *= 10;
3186            $self->{s_kwd} += $self->{nc} - 0x0030;
3187            
3188            ## Stay in the state.
3189            !!!next-input-character;
3190            redo A;
3191          } elsif ($self->{nc} == 0x003B) { # ;
3192            !!!cp (1013);
3193          !!!next-input-character;          !!!next-input-character;
3194            #
3195        } else {        } else {
3196            !!!cp (1014);
3197          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3198            ## Reconsume.
3199            #
3200        }        }
3201    
3202        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3203          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3204          my $c = $self->{column_prev};
3205          if ($charref_map->{$code}) {
3206            !!!cp (1015);
3207            !!!parse-error (type => 'invalid character reference',
3208                            text => (sprintf 'U+%04X', $code),
3209                            line => $l, column => $c);
3210            $code = $charref_map->{$code};
3211          } elsif ($code > 0x10FFFF) {
3212            !!!cp (1016);
3213            !!!parse-error (type => 'invalid character reference',
3214                            text => (sprintf 'U-%08X', $code),
3215                            line => $l, column => $c);
3216          $code = 0xFFFD;          $code = 0xFFFD;
3217          }
3218    
3219          if ($self->{prev_state} == DATA_STATE) {
3220            !!!cp (992);
3221            $self->{state} = $self->{prev_state};
3222            ## Reconsume.
3223            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3224                      line => $l, column => $c,
3225                     });
3226            redo A;
3227          } else {
3228            !!!cp (991);
3229            $self->{ca}->{value} .= chr $code;
3230            $self->{ca}->{has_reference} = 1;
3231            $self->{state} = $self->{prev_state};
3232            ## Reconsume.
3233            redo A;
3234          }
3235        } elsif ($self->{state} == HEXREF_X_STATE) {
3236          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3237              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3238              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3239            # 0..9, A..F, a..f
3240            !!!cp (990);
3241            $self->{state} = HEXREF_HEX_STATE;
3242            $self->{s_kwd} = 0;
3243            ## Reconsume.
3244            redo A;
3245          } else {
3246            !!!parse-error (type => 'bare hcro',
3247                            line => $self->{line_prev},
3248                            column => $self->{column_prev} - 2);
3249    
3250            ## NOTE: According to the spec algorithm, nothing is returned,
3251            ## and then "&#" followed by "X" or "x" is appended to the parent
3252            ## element or the attribute value in the later processing.
3253    
3254            if ($self->{prev_state} == DATA_STATE) {
3255              !!!cp (1005);
3256              $self->{state} = $self->{prev_state};
3257              ## Reconsume.
3258              !!!emit ({type => CHARACTER_TOKEN,
3259                        data => '&' . $self->{s_kwd},
3260                        line => $self->{line_prev},
3261                        column => $self->{column_prev} - length $self->{s_kwd},
3262                       });
3263              redo A;
3264            } else {
3265              !!!cp (989);
3266              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3267              $self->{state} = $self->{prev_state};
3268              ## Reconsume.
3269              redo A;
3270            }
3271          }
3272        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3273          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3274            # 0..9
3275            !!!cp (1002);
3276            $self->{s_kwd} *= 0x10;
3277            $self->{s_kwd} += $self->{nc} - 0x0030;
3278            ## Stay in the state.
3279            !!!next-input-character;
3280            redo A;
3281          } elsif (0x0061 <= $self->{nc} and
3282                   $self->{nc} <= 0x0066) { # a..f
3283            !!!cp (1003);
3284            $self->{s_kwd} *= 0x10;
3285            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3286            ## Stay in the state.
3287            !!!next-input-character;
3288            redo A;
3289          } elsif (0x0041 <= $self->{nc} and
3290                   $self->{nc} <= 0x0046) { # A..F
3291            !!!cp (1004);
3292            $self->{s_kwd} *= 0x10;
3293            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3294            ## Stay in the state.
3295            !!!next-input-character;
3296            redo A;
3297          } elsif ($self->{nc} == 0x003B) { # ;
3298            !!!cp (1006);
3299            !!!next-input-character;
3300            #
3301          } else {
3302            !!!cp (1007);
3303            !!!parse-error (type => 'no refc',
3304                            line => $self->{line},
3305                            column => $self->{column});
3306            ## Reconsume.
3307            #
3308          }
3309    
3310          my $code = $self->{s_kwd};
3311          my $l = $self->{line_prev};
3312          my $c = $self->{column_prev};
3313          if ($charref_map->{$code}) {
3314            !!!cp (1008);
3315            !!!parse-error (type => 'invalid character reference',
3316                            text => (sprintf 'U+%04X', $code),
3317                            line => $l, column => $c);
3318            $code = $charref_map->{$code};
3319        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3320          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3321            !!!parse-error (type => 'invalid character reference',
3322                            text => (sprintf 'U-%08X', $code),
3323                            line => $l, column => $c);
3324          $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};  
3325        }        }
3326          
3327        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3328      } else {          !!!cp (988);
3329        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3330        !!!back-next-input-character ($self->{next_char});          ## Reconsume.
3331        $self->{next_char} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3332        return undef;                    line => $l, column => $c,
3333      }                   });
3334    } elsif ((0x0041 <= $self->{next_char} and          redo A;
3335              $self->{next_char} <= 0x005A) or        } else {
3336             (0x0061 <= $self->{next_char} and          !!!cp (987);
3337              $self->{next_char} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3338      my $entity_name = chr $self->{next_char};          $self->{ca}->{has_reference} = 1;
3339      !!!next-input-character;          $self->{state} = $self->{prev_state};
3340            ## Reconsume.
3341      my $value = $entity_name;          redo A;
3342      my $match = 0;        }
3343      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3344      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3345              ## NOTE: Some number greater than the maximum length of entity name
3346      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3347             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3348             ((0x0041 <= $self->{next_char} and # a             (0x0061 <= $self->{nc} and # a
3349               $self->{next_char} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3350              (0x0061 <= $self->{next_char} and # a             (0x0030 <= $self->{nc} and # 0
3351               $self->{next_char} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3352              (0x0030 <= $self->{next_char} and # 0             $self->{nc} == 0x003B)) { # ;
3353               $self->{next_char} <= 0x0039) or # 9          our $EntityChar;
3354              $self->{next_char} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3355        $entity_name .= chr $self->{next_char};          if (defined $EntityChar->{$self->{s_kwd}}) {
3356        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3357          if ($self->{next_char} == 0x003B) { # ;              !!!cp (1020);
3358            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3359            $match = 1;              $self->{entity__match} = 1;
3360            !!!next-input-character;              !!!next-input-character;
3361            last;              #
3362              } else {
3363                !!!cp (1021);
3364                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3365                $self->{entity__match} = -1;
3366                ## Stay in the state.
3367                !!!next-input-character;
3368                redo A;
3369              }
3370          } else {          } else {
3371            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3372            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3373              $self->{entity__match} *= 2;
3374              ## Stay in the state.
3375            !!!next-input-character;            !!!next-input-character;
3376              redo A;
3377          }          }
       } else {  
         $value .= chr $self->{next_char};  
         $match *= 2;  
         !!!next-input-character;  
3378        }        }
3379      }  
3380              my $data;
3381      if ($match > 0) {        my $has_ref;
3382        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($self->{entity__match} > 0) {
3383      } elsif ($match < 0) {          !!!cp (1023);
3384        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3385        if ($in_attr and $match < -1) {          $has_ref = 1;
3386          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          #
3387          } elsif ($self->{entity__match} < 0) {
3388            !!!parse-error (type => 'no refc');
3389            if ($self->{prev_state} != DATA_STATE and # in attribute
3390                $self->{entity__match} < -1) {
3391              !!!cp (1024);
3392              $data = '&' . $self->{s_kwd};
3393              #
3394            } else {
3395              !!!cp (1025);
3396              $data = $self->{entity__value};
3397              $has_ref = 1;
3398              #
3399            }
3400        } else {        } else {
3401          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!cp (1026);
3402            !!!parse-error (type => 'bare ero',
3403                            line => $self->{line_prev},
3404                            column => $self->{column_prev} - length $self->{s_kwd});
3405            $data = '&' . $self->{s_kwd};
3406            #
3407          }
3408      
3409          ## NOTE: In these cases, when a character reference is found,
3410          ## it is consumed and a character token is returned, or, otherwise,
3411          ## nothing is consumed and returned, according to the spec algorithm.
3412          ## In this implementation, anything that has been examined by the
3413          ## tokenizer is appended to the parent element or the attribute value
3414          ## as string, either literal string when no character reference or
3415          ## entity-replaced string otherwise, in this stage, since any characters
3416          ## that would not be consumed are appended in the data state or in an
3417          ## appropriate attribute value state anyway.
3418    
3419          if ($self->{prev_state} == DATA_STATE) {
3420            !!!cp (986);
3421            $self->{state} = $self->{prev_state};
3422            ## Reconsume.
3423            !!!emit ({type => CHARACTER_TOKEN,
3424                      data => $data,
3425                      line => $self->{line_prev},
3426                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3427                     });
3428            redo A;
3429          } else {
3430            !!!cp (985);
3431            $self->{ca}->{value} .= $data;
3432            $self->{ca}->{has_reference} = 1 if $has_ref;
3433            $self->{state} = $self->{prev_state};
3434            ## Reconsume.
3435            redo A;
3436        }        }
3437      } else {      } else {
3438        !!!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};  
3439      }      }
3440    } else {    } # A  
3441      ## no characters are consumed  
3442      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3443      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3444    
3445  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3446    my $self = shift;    my $self = shift;
# Line 2355  sub _initialize_tree_constructor ($) { Line 3449  sub _initialize_tree_constructor ($) {
3449    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3450    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3451    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3452      $self->{document}->set_user_data (manakai_source_line => 1);
3453      $self->{document}->set_user_data (manakai_source_column => 1);
3454  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3455    
3456  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2374  sub _construct_tree ($) { Line 3470  sub _construct_tree ($) {
3470    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3471    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3472    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3473        
3474    !!!next-token;    !!!next-token;
3475    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
3476    undef $self->{form_element};    undef $self->{form_element};
3477    undef $self->{head_element};    undef $self->{head_element};
3478      undef $self->{head_element_inserted};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
3482      ## NOTE: The "initial" insertion mode.
3483    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3484    
3485      ## NOTE: The "before html" insertion mode.
3486    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3487      $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489      ## NOTE: The "before head" insertion mode and so on.
3490    $self->_tree_construction_main;    $self->_tree_construction_main;
3491  } # _construct_tree  } # _construct_tree
3492    
3493  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3494    my $self = shift;    my $self = shift;
3495    
3496      ## NOTE: "initial" insertion mode
3497    
3498    INITIAL: {    INITIAL: {
3499      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3500        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 2401  sub _tree_construction_initial ($) { Line 3502  sub _tree_construction_initial ($) {
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
3508            defined $token->{system_identifier}) {          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3512          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521          } else {
3522            !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3535                
3536        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537            !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3604            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3605            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3606            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3607            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
3610            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,            !!!cp ('t5');
           "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,  
           "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,  
           "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,  
           "HTML" => 1,  
         }->{$pubid}) {  
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615                !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618                !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623              !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625            } else {
3626              !!!cp ('t9');
3627          }          }
3628          } else {
3629            !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638              !!!cp ('t11');
3639            } else {
3640              !!!cp ('t12');
3641          }          }
3642          } else {
3643            !!!cp ('t13');
3644        }        }
3645                
3646        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3647        !!!next-token;        !!!next-token;
3648        return;        return;
3649      } elsif ({      } elsif ({
# Line 2529  sub _tree_construction_initial ($) { Line 3651  sub _tree_construction_initial ($) {
3651                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
3652                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3655          !!!parse-error (type => 'no DOCTYPE', token => $token);
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
3666            ## Stay in the phase            !!!cp ('t15');
3667              ## Stay in the insertion mode.
3668            !!!next-token;            !!!next-token;
3669            redo INITIAL;            redo INITIAL;
3670            } else {
3671              !!!cp ('t16');
3672          }          }
3673          } else {
3674            !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
3681        return;        return;
3682      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3683          !!!cp ('t18');
3684        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3685        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3686                
3687        ## Stay in the phase.        ## Stay in the insertion mode.
3688        !!!next-token;        !!!next-token;
3689        redo INITIAL;        redo INITIAL;
3690      } else {      } else {
3691        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
3692      }      }
3693    } # INITIAL    } # INITIAL
3694    
3695      die "$0: _tree_construction_initial: This should be never reached";
3696  } # _tree_construction_initial  } # _tree_construction_initial
3697    
3698  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3699    my $self = shift;    my $self = shift;
3700    
3701      ## NOTE: "before html" insertion mode.
3702        
3703    B: {    B: {
3704        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3706            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the phase          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
3710          redo B;          redo B;
3711        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
3712            !!!cp ('t20');
3713          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3714          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3715          ## Stay in the phase          ## Stay in the insertion mode.
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
3723              ## Stay in the phase              !!!cp ('t21');
3724                ## Stay in the insertion mode.
3725              !!!next-token;              !!!next-token;
3726              redo B;              redo B;
3727              } else {
3728                !!!cp ('t22');
3729            }            }
3730            } else {
3731              !!!cp ('t23');
3732          }          }
3733    
3734          $self->{application_cache_selection}->(undef);          $self->{application_cache_selection}->(undef);
3735    
3736          #          #
3737        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3738          if ($token->{tag_name} eq 'html' and          if ($token->{tag_name} eq 'html') {
3739              $token->{attributes}->{manifest}) {            my $root_element;
3740            $self->{application_cache_selection}            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741                 ->($token->{attributes}->{manifest}->{value});            $self->{document}->append_child ($root_element);
3742            ## ISSUE: No relative reference resolution?            push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745              if ($token->{attributes}->{manifest}) {
3746                !!!cp ('t24');
3747                $self->{application_cache_selection}
3748                    ->($token->{attributes}->{manifest}->{value});
3749                ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753              } else {
3754                !!!cp ('t25');
3755                $self->{application_cache_selection}->(undef);
3756              }
3757    
3758              !!!nack ('t25c');
3759    
3760              !!!next-token;
3761              return; ## Go to the "before head" insertion mode.
3762          } else {          } else {
3763            $self->{application_cache_selection}->(undef);            !!!cp ('t25.1');
3764              #
3765          }          }
   
         ## ISSUE: There is an issue in the spec  
         #  
3766        } elsif ({        } elsif ({
3767                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
3768                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
3769                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3770          $self->{application_cache_selection}->(undef);          !!!cp ('t26');
   
         ## ISSUE: There is an issue in the spec  
3771          #          #
3772        } else {        } else {
3773          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775    
3776        my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3777        $self->{document}->append_child ($root_element);      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778        push @{$self->{open_elements}}, [$root_element, 'html'];      $self->{document}->append_child ($root_element);
3779        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780        #redo B;  
3781        return; ## Go to the main phase.      $self->{application_cache_selection}->(undef);
3782    
3783        ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785        return; ## Go to the "before head" insertion mode.
3786    } # B    } # B
3787    
3788      die "$0: _tree_construction_root_element: This should never be reached";
3789  } # _tree_construction_root_element  } # _tree_construction_root_element
3790    
3791  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2638  sub _reset_insertion_mode ($) { Line 3800  sub _reset_insertion_mode ($) {
3800            
3801      ## Step 3      ## Step 3
3802      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"!?  
3803        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804          $last = 1;          $last = 1;
3805          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3806            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3807                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3808              #          } else {
3809            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3810          }          }
3811        }        }
3812              
3813        ## Step 4..13        ## Step 4..14
3814        my $new_mode = {        my $new_mode;
3815          if ($node->[1] & FOREIGN_EL) {
3816            !!!cp ('t28.1');
3817            ## NOTE: Strictly spaking, the line below only applies to MathML and
3818            ## SVG elements.  Currently the HTML syntax supports only MathML and
3819            ## SVG elements as foreigners.
3820            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821          } elsif ($node->[1] & TABLE_CELL_EL) {
3822            if ($last) {
3823              !!!cp ('t28.2');
3824              #
3825            } else {
3826              !!!cp ('t28.3');
3827              $new_mode = IN_CELL_IM;
3828            }
3829          } else {
3830            !!!cp ('t28.4');
3831            $new_mode = {
3832                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3833                        td => IN_CELL_IM,                        ## NOTE: |option| and |optgroup| do not set
3834                        th => IN_CELL_IM,                        ## insertion mode to "in select" by themselves.
3835                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3836                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3837                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2670  sub _reset_insertion_mode ($) { Line 3842  sub _reset_insertion_mode ($) {
3842                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3843                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3844                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3845                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3846          }
3847        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848                
3849        ## Step 14        ## Step 15
3850        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3851          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3852              !!!cp ('t29');
3853            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3854          } else {          } else {
3855              ## ISSUE: Can this state be reached?
3856              !!!cp ('t30');
3857            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3858          }          }
3859          return;          return;
3860          } else {
3861            !!!cp ('t31');
3862        }        }
3863                
3864        ## Step 15        ## Step 16
3865        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866                
3867        ## Step 16        ## Step 17
3868        $i--;        $i--;
3869        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3870                
3871        ## Step 17        ## Step 18
3872        redo S3;        redo S3;
3873      } # S3      } # S3
3874    
3875      die "$0: _reset_insertion_mode: This line should never be reached";
3876  } # _reset_insertion_mode  } # _reset_insertion_mode
3877    
3878  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2714  sub _tree_construction_main ($) { Line 3894  sub _tree_construction_main ($) {
3894      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3895      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3896        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3897            !!!cp ('t32');
3898          return;          return;
3899        }        }
3900      }      }
# Line 2728  sub _tree_construction_main ($) { Line 3909  sub _tree_construction_main ($) {
3909    
3910        ## Step 6        ## Step 6
3911        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3912            !!!cp ('t33_1');
3913          #          #
3914        } else {        } else {
3915          my $in_open_elements;          my $in_open_elements;
3916          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3917            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3918                !!!cp ('t33');
3919              $in_open_elements = 1;              $in_open_elements = 1;
3920              last OE;              last OE;
3921            }            }
3922          }          }
3923          if ($in_open_elements) {          if ($in_open_elements) {
3924              !!!cp ('t34');
3925            #            #
3926          } else {          } else {
3927              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3928              !!!cp ('t35');
3929            redo S4;            redo S4;
3930          }          }
3931        }        }
# Line 2762  sub _tree_construction_main ($) { Line 3948  sub _tree_construction_main ($) {
3948    
3949        ## Step 11        ## Step 11
3950        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3951            !!!cp ('t36');
3952          ## Step 7'          ## Step 7'
3953          $i++;          $i++;
3954          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3955                    
3956          redo S7;          redo S7;
3957        }        }
3958    
3959          !!!cp ('t37');
3960      } # S7      } # S7
3961    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3962    
3963    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3964      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3965        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3966            !!!cp ('t38');
3967          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3968          return;          return;
3969        }        }
3970      }      }
3971    
3972        !!!cp ('t39');
3973    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3974    
3975    my $parse_rcdata = sub ($$) {    my $insert;
3976      my ($content_model_flag, $insert) = @_;  
3977      my $parse_rcdata = sub ($) {
3978        my ($content_model_flag) = @_;
3979    
3980      ## Step 1      ## Step 1
3981      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3982      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3983    
3984      ## Step 2      ## Step 2
     $insert->($el); # /context node/->append_child ($el)  
   
     ## Step 3  
3985      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3986      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3987    
3988      ## Step 4      ## Step 3, 4
3989      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
3990    
3991      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {  
       ## 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});  
     } else {  
       die "$0: $content_model_flag in parse_rcdata";  
     }  
3992      !!!next-token;      !!!next-token;
3993    }; # $parse_rcdata    }; # $parse_rcdata
3994    
3995    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
3996      my $insert = $_[0];      ## Step 1
3997      my $script_el;      my $script_el;
3998      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
3999    
4000        ## Step 2
4001      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4002    
4003        ## Step 3
4004        ## TODO: Mark as "already executed", if ...
4005    
4006        ## Step 4
4007        $insert->($script_el);
4008    
4009        ## ISSUE: $script_el is not put into the stack
4010        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4011    
4012        ## Step 5
4013      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4014      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
4015    
4016      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4017          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       ## Ignore the token  
     } else {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       ## TODO: mark as "already executed"  
     } else {  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
4018    
4019        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
4020      !!!next-token;      !!!next-token;
4021    }; # $script_start_tag    }; # $script_start_tag
4022    
4023      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4024      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4025      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4026      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4027    
4028    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4029      my $tag_name = shift;      my $end_tag_token = shift;
4030        my $tag_name = $end_tag_token->{tag_name};
4031    
4032        ## NOTE: The adoption agency algorithm (AAA).
4033    
4034      FET: {      FET: {
4035        ## Step 1        ## Step 1
4036        my $formatting_element;        my $formatting_element;
4037        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4038        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4039          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4040              !!!cp ('t52');
4041              last AFE;
4042            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4043                         eq $tag_name) {
4044              !!!cp ('t51');
4045            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4046            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4047            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4048          }          }
4049        } # AFE        } # AFE
4050        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4051          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4052            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4053          ## Ignore the token          ## Ignore the token
4054          !!!next-token;          !!!next-token;
4055          return;          return;
# Line 2900  sub _tree_construction_main ($) { Line 4061  sub _tree_construction_main ($) {
4061          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4062          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4063            if ($in_scope) {            if ($in_scope) {
4064                !!!cp ('t54');
4065              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4066              last INSCOPE;              last INSCOPE;
4067            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4068              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4069                !!!parse-error (type => 'unmatched end tag',
4070                                text => $token->{tag_name},
4071                                token => $end_tag_token);
4072              ## Ignore the token              ## Ignore the token
4073              !!!next-token;              !!!next-token;
4074              return;              return;
4075            }            }
4076          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4077                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4078            $in_scope = 0;            $in_scope = 0;
4079          }          }
4080        } # INSCOPE        } # INSCOPE
4081        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4082          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4083            !!!parse-error (type => 'unmatched end tag',
4084                            text => $token->{tag_name},
4085                            token => $end_tag_token);
4086          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4087          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4088          return;          return;
4089        }        }
4090        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4091          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4092            !!!parse-error (type => 'not closed',
4093                            text => $self->{open_elements}->[-1]->[0]
4094                                ->manakai_local_name,
4095                            token => $end_tag_token);
4096        }        }
4097                
4098        ## Step 2        ## Step 2
# Line 2930  sub _tree_construction_main ($) { Line 4100  sub _tree_construction_main ($) {
4100        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4101        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4102          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4103          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4104              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4105              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4106               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4107              !!!cp ('t59');
4108            $furthest_block = $node;            $furthest_block = $node;
4109            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4110              ## NOTE: The topmost (eldest) node.
4111          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4112              !!!cp ('t60');
4113            last OE;            last OE;
4114          }          }
4115        } # OE        } # OE
4116                
4117        ## Step 3        ## Step 3
4118        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4119            !!!cp ('t61');
4120          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4121          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4122          !!!next-token;          !!!next-token;
# Line 2955  sub _tree_construction_main ($) { Line 4129  sub _tree_construction_main ($) {
4129        ## Step 5        ## Step 5
4130        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4131        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4132            !!!cp ('t62');
4133          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4134        }        }
4135                
# Line 2977  sub _tree_construction_main ($) { Line 4152  sub _tree_construction_main ($) {
4152          S7S2: {          S7S2: {
4153            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4154              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4155                  !!!cp ('t63');
4156                $node_i_in_active = $_;                $node_i_in_active = $_;
4157                last S7S2;                last S7S2;
4158              }              }
# Line 2990  sub _tree_construction_main ($) { Line 4166  sub _tree_construction_main ($) {
4166                    
4167          ## Step 4          ## Step 4
4168          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4169              !!!cp ('t64');
4170            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4171          }          }
4172                    
4173          ## Step 5          ## Step 5
4174          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4175              !!!cp ('t65');
4176            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4177            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4178            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 3012  sub _tree_construction_main ($) { Line 4190  sub _tree_construction_main ($) {
4190        } # S7          } # S7  
4191                
4192        ## Step 8        ## Step 8
4193        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4194            my $foster_parent_element;
4195            my $next_sibling;
4196            OE: for (reverse 0..$#{$self->{open_elements}}) {
4197              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4198                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4199                                 if (defined $parent and $parent->node_type == 1) {
4200                                   !!!cp ('t65.1');
4201                                   $foster_parent_element = $parent;
4202                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4203                                 } else {
4204                                   !!!cp ('t65.2');
4205                                   $foster_parent_element
4206                                     = $self->{open_elements}->[$_ - 1]->[0];
4207                                 }
4208                                 last OE;
4209                               }
4210                             } # OE
4211                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4212                               unless defined $foster_parent_element;
4213            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4214            $open_tables->[-1]->[1] = 1; # tainted
4215          } else {
4216            !!!cp ('t65.3');
4217            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4218          }
4219                
4220        ## Step 9        ## Step 9
4221        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 3029  sub _tree_construction_main ($) { Line 4232  sub _tree_construction_main ($) {
4232        my $i;        my $i;
4233        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4234          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4235              !!!cp ('t66');
4236            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4237            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4238          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4239              !!!cp ('t67');
4240            $i = $_;            $i = $_;
4241          }          }
4242        } # AFE        } # AFE
# Line 3041  sub _tree_construction_main ($) { Line 4246  sub _tree_construction_main ($) {
4246        undef $i;        undef $i;
4247        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4248          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4249              !!!cp ('t68');
4250            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4251            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4252          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4253              !!!cp ('t69');
4254            $i = $_;            $i = $_;
4255          }          }
4256        } # OE        } # OE
4257        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4258                
4259        ## Step 14        ## Step 14
4260        redo FET;        redo FET;
4261      } # FET      } # FET
4262    }; # $formatting_end_tag    }; # $formatting_end_tag
4263    
4264    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4265      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4266    }; # $insert_to_current    }; # $insert_to_current
4267    
4268    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4269                         my $child = shift;      my $child = shift;
4270                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4271                              table => 1, tbody => 1, tfoot => 1,        # MUST
4272                              thead => 1, tr => 1,        my $foster_parent_element;
4273                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4274                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4275                           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') {  
4276                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4277                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4278                                   !!!cp ('t70');
4279                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4280                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4281                               } else {                               } else {
4282                                   !!!cp ('t71');
4283                                 $foster_parent_element                                 $foster_parent_element
4284                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4285                               }                               }
# Line 3084  sub _tree_construction_main ($) { Line 4290  sub _tree_construction_main ($) {
4290                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4291                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4292                             ($child, $next_sibling);                             ($child, $next_sibling);
4293                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4294                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4295                         }        !!!cp ('t72');
4296          $self->{open_elements}->[-1]->[0]->append_child ($child);
4297        }
4298    }; # $insert_to_foster    }; # $insert_to_foster
4299    
4300    my $insert;    ## NOTE: Insert a character (MUST): When a character is inserted, if
4301      ## the last node that was inserted by the parser is a Text node and
4302      ## the character has to be inserted after that node, then the
4303      ## character is appended to the Text node.  However, if any other
4304      ## node is inserted by the parser, then a new Text node is created
4305      ## and the character is appended as that Text node.  If I'm not
4306      ## wrong, for a parser with scripting disabled, there are only two
4307      ## cases where this occurs.  One is the case where an element node
4308      ## is inserted to the |head| element.  This is covered by using the
4309      ## |$self->{head_element_inserted}| flag.  Another is the case where
4310      ## an element or comment is inserted into the |table| subtree while
4311      ## foster parenting happens.  This is covered by using the [2] flag
4312      ## of the |$open_tables| structure.  All other cases are handled
4313      ## simply by calling |manakai_append_text| method.
4314    
4315      ## TODO: |<body><script>document.write("a<br>");
4316      ## document.body.removeChild (document.body.lastChild);
4317      ## document.write ("b")</script>|
4318    
4319    B: {    B: while (1) {
4320      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4321        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
4322          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4323        ## Ignore the token        ## Ignore the token
4324        ## Stay in the phase        ## Stay in the phase
4325        !!!next-token;        !!!next-token;
4326        redo B;        next B;
     } elsif ($token->{type} == END_OF_FILE_TOKEN) {  
       if ($self->{insertion_mode} & AFTER_HTML_IMS) {  
         #  
       } 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;  
4327      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4328               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4329        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4330          ## Turn into the main phase          !!!cp ('t79');
4331          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4332          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4333        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4334          ## Turn into the main phase          !!!cp ('t80');
4335          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4336          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4337          } else {
4338            !!!cp ('t81');
4339        }        }
4340    
4341  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
4342  ## 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');  
       }  
4343        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4344        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4345          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4346              !!!cp ('t84');
4347            $top_el->set_attribute_ns            $top_el->set_attribute_ns
4348              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
4349               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4350          }          }
4351        }        }
4352          !!!nack ('t84.1');
4353        !!!next-token;        !!!next-token;
4354        redo B;        next B;
4355      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4356        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4357        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4358            !!!cp ('t85');
4359          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
4360        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4361            !!!cp ('t86');
4362          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
4363        } else {        } else {
4364            !!!cp ('t87');
4365          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4366            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4367        }        }
4368        !!!next-token;        !!!next-token;
4369        redo B;        next B;
4370      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4371        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4372          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4373            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          delete $self->{ignore_newline};
4374    
4375            if (length $token->{data}) {
4376              !!!cp ('t43');
4377              $self->{open_elements}->[-1]->[0]->manakai_append_text
4378                  ($token->{data});
4379            } else {
4380              !!!cp ('t43.1');
4381            }
4382            !!!next-token;
4383            next B;
4384          } elsif ($token->{type} == END_TAG_TOKEN) {
4385            delete $self->{ignore_newline};
4386    
4387            if ($token->{tag_name} eq 'script') {
4388              !!!cp ('t50');
4389              
4390              ## Para 1-2
4391              my $script = pop @{$self->{open_elements}};
4392              
4393              ## Para 3
4394              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4395    
4396              ## Para 4
4397              ## TODO: $old_insertion_point = $current_insertion_point;
4398              ## TODO: $current_insertion_point = just before $self->{nc};
4399    
4400              ## Para 5
4401              ## TODO: Run the $script->[0].
4402    
4403              ## Para 6
4404              ## TODO: $current_insertion_point = $old_insertion_point;
4405    
4406              ## Para 7
4407              ## TODO: if ($pending_external_script) {
4408                ## TODO: ...
4409              ## TODO: }
4410    
4411              !!!next-token;
4412              next B;
4413            } else {
4414              !!!cp ('t42');
4415    
4416              pop @{$self->{open_elements}};
4417    
4418              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4419              !!!next-token;
4420              next B;
4421            }
4422          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4423            delete $self->{ignore_newline};
4424    
4425            !!!cp ('t44');
4426            !!!parse-error (type => 'not closed',
4427                            text => $self->{open_elements}->[-1]->[0]
4428                                ->manakai_local_name,
4429                            token => $token);
4430    
4431            #if ($self->{open_elements}->[-1]->[1] & SCRIPT_EL) {
4432            #  ## TODO: Mark as "already executed"
4433            #}
4434    
4435            pop @{$self->{open_elements}};
4436    
4437            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4438            ## Reprocess.
4439            next B;
4440          } else {
4441            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4442          }
4443        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4444          if ($token->{type} == CHARACTER_TOKEN) {
4445            !!!cp ('t87.1');
4446            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4447            !!!next-token;
4448            next B;
4449          } elsif ($token->{type} == START_TAG_TOKEN) {
4450            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4451                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4452                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4453                ($token->{tag_name} eq 'svg' and
4454                 $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4455              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4456              !!!cp ('t87.2');
4457              #
4458            } elsif ({
4459                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4460                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4461                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4462                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4463                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4464                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4465                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4466                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4467                     }->{$token->{tag_name}}) {
4468              !!!cp ('t87.2');
4469              !!!parse-error (type => 'not closed',
4470                              text => $self->{open_elements}->[-1]->[0]
4471                                  ->manakai_local_name,
4472                              token => $token);
4473    
4474              pop @{$self->{open_elements}}
4475                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4476    
4477              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4478              ## Reprocess.
4479              next B;
4480            } else {
4481              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4482              my $tag_name = $token->{tag_name};
4483              if ($nsuri eq $SVG_NS) {
4484                $tag_name = {
4485                   altglyph => 'altGlyph',
4486                   altglyphdef => 'altGlyphDef',
4487                   altglyphitem => 'altGlyphItem',
4488                   animatecolor => 'animateColor',
4489                   animatemotion => 'animateMotion',
4490                   animatetransform => 'animateTransform',
4491                   clippath => 'clipPath',
4492                   feblend => 'feBlend',
4493                   fecolormatrix => 'feColorMatrix',
4494                   fecomponenttransfer => 'feComponentTransfer',
4495                   fecomposite => 'feComposite',
4496                   feconvolvematrix => 'feConvolveMatrix',
4497                   fediffuselighting => 'feDiffuseLighting',
4498                   fedisplacementmap => 'feDisplacementMap',
4499                   fedistantlight => 'feDistantLight',
4500                   feflood => 'feFlood',
4501                   fefunca => 'feFuncA',
4502                   fefuncb => 'feFuncB',
4503                   fefuncg => 'feFuncG',
4504                   fefuncr => 'feFuncR',
4505                   fegaussianblur => 'feGaussianBlur',
4506                   feimage => 'feImage',
4507                   femerge => 'feMerge',
4508                   femergenode => 'feMergeNode',
4509                   femorphology => 'feMorphology',
4510                   feoffset => 'feOffset',
4511                   fepointlight => 'fePointLight',
4512                   fespecularlighting => 'feSpecularLighting',
4513                   fespotlight => 'feSpotLight',
4514                   fetile => 'feTile',
4515                   feturbulence => 'feTurbulence',
4516                   foreignobject => 'foreignObject',
4517                   glyphref => 'glyphRef',
4518                   lineargradient => 'linearGradient',
4519                   radialgradient => 'radialGradient',
4520                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4521                   textpath => 'textPath',  
4522                }->{$tag_name} || $tag_name;
4523              }
4524    
4525              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4526    
4527              ## "adjust foreign attributes" - done in insert-element-f
4528    
4529              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4530    
4531              if ($self->{self_closing}) {
4532                pop @{$self->{open_elements}};
4533                !!!ack ('t87.3');
4534              } else {
4535                !!!cp ('t87.4');
4536              }
4537    
4538              !!!next-token;
4539              next B;
4540            }
4541          } elsif ($token->{type} == END_TAG_TOKEN) {
4542            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4543            !!!cp ('t87.5');
4544            #
4545          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4546            !!!cp ('t87.6');
4547            !!!parse-error (type => 'not closed',
4548                            text => $self->{open_elements}->[-1]->[0]
4549                                ->manakai_local_name,
4550                            token => $token);
4551    
4552            pop @{$self->{open_elements}}
4553                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4554    
4555            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4556    
4557            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4558            ## Reprocess.
4559            next B;
4560          } else {
4561            die "$0: $token->{type}: Unknown token type";        
4562          }
4563        }
4564    
4565        if ($self->{insertion_mode} & HEAD_IMS) {
4566          if ($token->{type} == CHARACTER_TOKEN) {
4567            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4568              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4569                if ($self->{head_element_inserted}) {
4570                  !!!cp ('t88.3');
4571                  $self->{open_elements}->[-1]->[0]->append_child
4572                    ($self->{document}->create_text_node ($1));
4573                  delete $self->{head_element_inserted};
4574                  ## NOTE: |</head> <link> |
4575                  #
4576                } else {
4577                  !!!cp ('t88.2');
4578                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4579                  ## NOTE: |</head> &#x20;|
4580                  #
4581                }
4582              } else {
4583                !!!cp ('t88.1');
4584                ## Ignore the token.
4585                #
4586              }
4587            unless (length $token->{data}) {            unless (length $token->{data}) {
4588                !!!cp ('t88');
4589              !!!next-token;              !!!next-token;
4590              redo B;              next B;
4591            }            }
4592    ## TODO: set $token->{column} appropriately
4593          }          }
4594    
4595          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4596              !!!cp ('t89');
4597            ## As if <head>            ## As if <head>
4598            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4599            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4600            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4601                  [$self->{head_element}, $el_category->{head}];
4602    
4603            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4604            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4605    
4606            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4607          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4608              !!!cp ('t90');
4609            ## As if </noscript>            ## As if </noscript>
4610            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4611            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4612                        
4613            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4614            ## As if </head>            ## As if </head>
# Line 3195  sub _tree_construction_main ($) { Line 4616  sub _tree_construction_main ($) {
4616    
4617            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4618          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4619              !!!cp ('t91');
4620            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4621    
4622            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4623            } else {
4624              !!!cp ('t92');
4625          }          }
4626    
4627              ## "after head" insertion mode          ## "after head" insertion mode
4628              ## As if <body>          ## As if <body>
4629              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4630              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4631              ## reprocess          ## reprocess
4632              redo B;          next B;
4633            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4634              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4635                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4636                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!cp ('t93');
4637                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4638                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];              $self->{open_elements}->[-1]->[0]->append_child
4639                  $self->{insertion_mode} = IN_HEAD_IM;                  ($self->{head_element});
4640                  !!!next-token;              push @{$self->{open_elements}},
4641                  redo B;                  [$self->{head_element}, $el_category->{head}];
4642                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4643                  #              !!!nack ('t93.1');
4644                } else {              !!!next-token;
4645                  !!!parse-error (type => 'in head:head'); # or in head noscript              next B;
4646                  ## Ignore the token            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4647                  !!!next-token;              !!!cp ('t93.2');
4648                  redo B;              !!!parse-error (type => 'after head', text => 'head',
4649                }                              token => $token);
4650              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              ## Ignore the token
4651                ## As if <head>              !!!nack ('t93.3');
4652                !!!create-element ($self->{head_element}, 'head');              !!!next-token;
4653                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              next B;
4654                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4655                !!!cp ('t95');
4656                !!!parse-error (type => 'in head:head',
4657                                token => $token); # or in head noscript
4658                ## Ignore the token
4659                !!!nack ('t95.1');
4660                !!!next-token;
4661                next B;
4662              }
4663            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4664              !!!cp ('t96');
4665              ## As if <head>
4666              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4667              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4668              push @{$self->{open_elements}},
4669                  [$self->{head_element}, $el_category->{head}];
4670    
4671                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4672                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4673              }          } else {
4674              !!!cp ('t97');
4675            }
4676    
4677              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4678                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4679                  ## As if </noscript>              !!!cp ('t98');
4680                  pop @{$self->{open_elements}};              ## As if </noscript>
4681                  !!!parse-error (type => 'in noscript:base');              pop @{$self->{open_elements}};
4682                              !!!parse-error (type => 'in noscript', text => 'base',
4683                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4684                  ## Reprocess in the "in head" insertion mode...            
4685                }              $self->{insertion_mode} = IN_HEAD_IM;
4686                ## Reprocess in the "in head" insertion mode...
4687              } else {
4688                !!!cp ('t99');
4689              }
4690    
4691                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4692                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4693                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t100');
4694                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4695                }                              text => $token->{tag_name}, token => $token);
4696                !!!insert-element ($token->{tag_name}, $token->{attributes});              push @{$self->{open_elements}},
4697                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  [$self->{head_element}, $el_category->{head}];
4698                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4699                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4700                !!!next-token;              !!!cp ('t101');
4701                redo B;            }
4702              } elsif ($token->{tag_name} eq 'link') {            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4703                ## NOTE: There is a "as if in head" code clone.            pop @{$self->{open_elements}};
4704                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}} # <head>
4705                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4706                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!nack ('t101.1');
4707                }            !!!next-token;
4708                !!!insert-element ($token->{tag_name}, $token->{attributes});            next B;
4709                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          } elsif ($token->{tag_name} eq 'link') {
4710                pop @{$self->{open_elements}}            ## NOTE: There is a "as if in head" code clone.
4711                    if $self->{insertion_mode} == AFTER_HEAD_IM;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4712                !!!next-token;              !!!cp ('t102');
4713                redo B;              !!!parse-error (type => 'after head',
4714              } elsif ($token->{tag_name} eq 'meta') {                              text => $token->{tag_name}, token => $token);
4715                ## NOTE: There is a "as if in head" code clone.              push @{$self->{open_elements}},
4716                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  [$self->{head_element}, $el_category->{head}];
4717                  !!!parse-error (type => 'after head:'.$token->{tag_name});              $self->{head_element_inserted} = 1;
4718                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4719                }              !!!cp ('t103');
4720                !!!insert-element ($token->{tag_name}, $token->{attributes});            }
4721                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4722              pop @{$self->{open_elements}};
4723              pop @{$self->{open_elements}} # <head>
4724                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4725              !!!ack ('t103.1');
4726              !!!next-token;
4727              next B;
4728            } elsif ($token->{tag_name} eq 'command' or
4729                     $token->{tag_name} eq 'eventsource') {
4730              if ($self->{insertion_mode} == IN_HEAD_IM) {
4731                ## NOTE: If the insertion mode at the time of the emission
4732                ## of the token was "before head", $self->{insertion_mode}
4733                ## is already changed to |IN_HEAD_IM|.
4734    
4735                ## NOTE: There is a "as if in head" code clone.
4736                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4737                pop @{$self->{open_elements}};
4738                pop @{$self->{open_elements}} # <head>
4739                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4740                !!!ack ('t103.2');
4741                !!!next-token;
4742                next B;
4743              } else {
4744                ## NOTE: "in head noscript" or "after head" insertion mode
4745                ## - in these cases, these tags are treated as same as
4746                ## normal in-body tags.
4747                !!!cp ('t103.3');
4748                #
4749              }
4750            } elsif ($token->{tag_name} eq 'meta') {
4751              ## NOTE: There is a "as if in head" code clone.
4752              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4753                !!!cp ('t104');
4754                !!!parse-error (type => 'after head',
4755                                text => $token->{tag_name}, token => $token);
4756                push @{$self->{open_elements}},
4757                    [$self->{head_element}, $el_category->{head}];
4758                $self->{head_element_inserted} = 1;
4759              } else {
4760                !!!cp ('t105');
4761              }
4762              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4763              my $meta_el = pop @{$self->{open_elements}};
4764    
4765                unless ($self->{confident}) {                unless ($self->{confident}) {
4766                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4767                      !!!cp ('t106');
4768                      ## NOTE: Whether the encoding is supported or not is handled
4769                      ## in the {change_encoding} callback.
4770                    $self->{change_encoding}                    $self->{change_encoding}
4771                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4772                             $token);
4773                                        
4774                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4775                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4776                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4777                                                 ->{has_reference});                                                 ->{has_reference});
4778                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4779                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4780                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4781                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4782                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4783                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4784                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4785                        !!!cp ('t107');
4786                        ## NOTE: Whether the encoding is supported or not is handled
4787                        ## in the {change_encoding} callback.
4788                      $self->{change_encoding}                      $self->{change_encoding}
4789                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4790                               $token);
4791                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4792                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4793                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
4794                                                     ->{has_reference});                                                     ->{has_reference});
4795                      } else {
4796                        !!!cp ('t108');
4797                    }                    }
4798                  }                  }
4799                } else {                } else {
4800                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
4801                      !!!cp ('t109');
4802                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4803                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4804                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4805                                                 ->{has_reference});                                                 ->{has_reference});
4806                  }                  }
4807                  if ($token->{attributes}->{content}) {                  if ($token->{attributes}->{content}) {
4808                      !!!cp ('t110');
4809                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4810                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4811                                             $token->{attributes}->{content}                                             $token->{attributes}->{content}
# Line 3314  sub _tree_construction_main ($) { Line 4813  sub _tree_construction_main ($) {
4813                  }                  }
4814                }                }
4815    
4816                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4817                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4818                  !!!ack ('t110.1');
4819                !!!next-token;                !!!next-token;
4820                redo B;                next B;
4821              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4822                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4823                  ## As if </noscript>              !!!cp ('t111');
4824                  pop @{$self->{open_elements}};              ## As if </noscript>
4825                  !!!parse-error (type => 'in noscript:title');              pop @{$self->{open_elements}};
4826                              !!!parse-error (type => 'in noscript', text => 'title',
4827                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4828                  ## Reprocess in the "in head" insertion mode...            
4829                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4830                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4831                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4832                }              !!!cp ('t112');
4833                !!!parse-error (type => 'after head',
4834                                text => $token->{tag_name}, token => $token);
4835                push @{$self->{open_elements}},
4836                    [$self->{head_element}, $el_category->{head}];
4837                $self->{head_element_inserted} = 1;
4838              } else {
4839                !!!cp ('t113');
4840              }
4841    
4842                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4843                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4844                    : $self->{open_elements}->[-1]->[0];            ## ISSUE: A spec bug [Bug 6038]
4845                $parse_rcdata->(RCDATA_CONTENT_MODEL,            splice @{$self->{open_elements}}, -2, 1, () # <head>
4846                                sub { $parent->append_child ($_[0]) });                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4847                pop @{$self->{open_elements}}            next B;
4848                    if $self->{insertion_mode} == AFTER_HEAD_IM;          } elsif ($token->{tag_name} eq 'style' or
4849                redo B;                   $token->{tag_name} eq 'noframes') {
4850              } elsif ($token->{tag_name} eq 'style') {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4851                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## insertion mode IN_HEAD_IM)
4852                ## insertion mode IN_HEAD_IM)            ## NOTE: There is a "as if in head" code clone.
4853                ## NOTE: There is a "as if in head" code clone.            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4854                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!cp ('t114');
4855                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4856                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4857                }              push @{$self->{open_elements}},
4858                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                  [$self->{head_element}, $el_category->{head}];
4859                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4860                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4861                redo B;              !!!cp ('t115');
4862              } elsif ($token->{tag_name} eq 'noscript') {            }
4863              $parse_rcdata->(CDATA_CONTENT_MODEL);
4864              ## ISSUE: A spec bug [Bug 6038]
4865              splice @{$self->{open_elements}}, -2, 1, () # <head>
4866                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4867              next B;
4868            } elsif ($token->{tag_name} eq 'noscript') {
4869                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4870                    !!!cp ('t116');
4871                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4872                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4873                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4874                    !!!nack ('t116.1');
4875                  !!!next-token;                  !!!next-token;
4876                  redo B;                  next B;
4877                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4878                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4879                    !!!parse-error (type => 'in noscript', text => 'noscript',
4880                                    token => $token);
4881                  ## Ignore the token                  ## Ignore the token
4882                    !!!nack ('t117.1');
4883                  !!!next-token;                  !!!next-token;
4884                  redo B;                  next B;
4885                } else {                } else {
4886                    !!!cp ('t118');
4887                  #                  #
4888                }                }
4889              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4890                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4891                  ## As if </noscript>              !!!cp ('t119');
4892                  pop @{$self->{open_elements}};              ## As if </noscript>
4893                  !!!parse-error (type => 'in noscript:script');              pop @{$self->{open_elements}};
4894                              !!!parse-error (type => 'in noscript', text => 'script',
4895                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4896                  ## Reprocess in the "in head" insertion mode...            
4897                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4898                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4899                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4900                }              !!!cp ('t120');
4901                !!!parse-error (type => 'after head',
4902                                text => $token->{tag_name}, token => $token);
4903                push @{$self->{open_elements}},
4904                    [$self->{head_element}, $el_category->{head}];
4905                $self->{head_element_inserted} = 1;
4906              } else {
4907                !!!cp ('t121');
4908              }
4909    
4910                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4911                $script_start_tag->($insert_to_current);            $script_start_tag->();
4912                pop @{$self->{open_elements}}            ## ISSUE: A spec bug  [Bug 6038]
4913                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
4914                redo B;                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4915              } elsif ($token->{tag_name} eq 'body' or            next B;
4916                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
4917                     $token->{tag_name} eq 'frameset') {
4918                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4919                    !!!cp ('t122');
4920                  ## As if </noscript>                  ## As if </noscript>
4921                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4922                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4923                                    text => $token->{tag_name}, token => $token);
4924                                    
4925                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4926                  ## As if </head>                  ## As if </head>
# Line 3397  sub _tree_construction_main ($) { Line 4928  sub _tree_construction_main ($) {
4928                                    
4929                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4930                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4931                    !!!cp ('t124');
4932                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4933                                    
4934                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4935                  } else {
4936                    !!!cp ('t125');
4937                }                }
4938    
4939                ## "after head" insertion mode                ## "after head" insertion mode
4940                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4941                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4942                    !!!cp ('t126');
4943                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
4944                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
4945                    !!!cp ('t127');
4946                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4947                } else {                } else {
4948                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4949                }                }
4950                  !!!nack ('t127.1');
4951                !!!next-token;                !!!next-token;
4952                redo B;                next B;
4953              } else {              } else {
4954                  !!!cp ('t128');
4955                #                #
4956              }              }
4957    
4958              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4959                  !!!cp ('t129');
4960                ## As if </noscript>                ## As if </noscript>
4961                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4962                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4963                                  text => $token->{tag_name}, token => $token);
4964                                
4965                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4966                ## As if </head>                ## As if </head>
# Line 3428  sub _tree_construction_main ($) { Line 4968  sub _tree_construction_main ($) {
4968    
4969                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4970              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4971                  !!!cp ('t130');
4972                ## As if </head>                ## As if </head>
4973                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4974    
4975                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4976                } else {
4977                  !!!cp ('t131');
4978              }              }
4979    
4980              ## "after head" insertion mode              ## "after head" insertion mode
4981              ## As if <body>              ## As if <body>
4982              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4983              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4984              ## reprocess              ## reprocess
4985              redo B;              !!!ack-later;
4986                next B;
4987            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4988              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4989                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4990                    !!!cp ('t132');
4991                  ## As if <head>                  ## As if <head>
4992                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4993                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4994                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
4995                        [$self->{head_element}, $el_category->{head}];
4996    
4997                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4998                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4999                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5000                  !!!next-token;                  !!!next-token;
5001                  redo B;                  next B;
5002                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5003                    !!!cp ('t133');
5004                  ## As if </noscript>                  ## As if </noscript>
5005                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5006                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/',
5007                                    text => 'head', token => $token);
5008                                    
5009                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5011                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5012                  !!!next-token;                  !!!next-token;
5013                  redo B;                  next B;
5014                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5015                    !!!cp ('t134');
5016                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5017                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5018                  !!!next-token;                  !!!next-token;
5019                  redo B;                  next B;
5020                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021                    !!!cp ('t134.1');
5022                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5023                                    token => $token);
5024                    ## Ignore the token
5025                    !!!next-token;
5026                    next B;
5027                } else {                } else {
5028                  #                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5029                }                }
5030              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5031                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5032                    !!!cp ('t136');
5033                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5034                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5035                  !!!next-token;                  !!!next-token;
5036                  redo B;                  next B;
5037                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5038                  !!!parse-error (type => 'unmatched end tag:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
5039                    !!!cp ('t137');
5040                    !!!parse-error (type => 'unmatched end tag',
5041                                    text => 'noscript', token => $token);
5042                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5043                  !!!next-token;                  !!!next-token;
5044                  redo B;                  next B;
5045                } else {                } else {
5046                    !!!cp ('t138');
5047                  #                  #
5048                }                }
5049              } elsif ({              } elsif ({
5050                        body => 1, html => 1,                        body => 1, html => 1,
5051                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5052                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5053                  ## As if <head>                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5054                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_IM or
5055                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5056                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!cp ('t140');
5057                    !!!parse-error (type => 'unmatched end tag',
5058                  $self->{insertion_mode} = IN_HEAD_IM;                                  text => $token->{tag_name}, token => $token);
                 ## Reprocess in the "in head" insertion mode...  
               } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5059                  ## Ignore the token                  ## Ignore the token
5060                  !!!next-token;                  !!!next-token;
5061                  redo B;                  next B;
5062                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5063                    !!!cp ('t140.1');
5064                    !!!parse-error (type => 'unmatched end tag',
5065                                    text => $token->{tag_name}, token => $token);
5066                    ## Ignore the token
5067                    !!!next-token;
5068                    next B;
5069                  } else {
5070                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5071                }                }
5072                              } elsif ($token->{tag_name} eq 'p') {
5073                #                !!!cp ('t142');
5074              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5075                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5076                       }->{$token->{tag_name}}) {                ## Ignore the token
5077                  !!!next-token;
5078                  next B;
5079                } elsif ($token->{tag_name} eq 'br') {
5080                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5081                  ## As if <head>                  !!!cp ('t142.2');
5082                  !!!create-element ($self->{head_element}, 'head');                  ## (before head) as if <head>, (in head) as if </head>
5083                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5084                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5085                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5086      
5087                    ## Reprocess in the "after head" insertion mode...
5088                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5089                    !!!cp ('t143.2');
5090                    ## As if </head>
5091                    pop @{$self->{open_elements}};
5092                    $self->{insertion_mode} = AFTER_HEAD_IM;
5093      
5094                    ## Reprocess in the "after head" insertion mode...
5095                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5096                    !!!cp ('t143.3');
5097                    ## ISSUE: Two parse errors for <head><noscript></br>
5098                    !!!parse-error (type => 'unmatched end tag',
5099                                    text => 'br', token => $token);
5100                    ## As if </noscript>
5101                    pop @{$self->{open_elements}};
5102                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5103    
5104                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5105                }                  ## As if </head>
5106                    pop @{$self->{open_elements}};
5107                    $self->{insertion_mode} = AFTER_HEAD_IM;
5108    
5109                #                  ## Reprocess in the "after head" insertion mode...
5110              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5111                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
5112                  #                  #
5113                } else {                } else {
5114                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5115                }                }
5116    
5117                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5118                  !!!parse-error (type => 'unmatched end tag',
5119                                  text => 'br', token => $token);
5120                  ## Ignore the token
5121                  !!!next-token;
5122                  next B;
5123                } else {
5124                  !!!cp ('t145');
5125                  !!!parse-error (type => 'unmatched end tag',
5126                                  text => $token->{tag_name}, token => $token);
5127                  ## Ignore the token
5128                  !!!next-token;
5129                  next B;
5130              }              }
5131    
5132              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5133                  !!!cp ('t146');
5134                ## As if </noscript>                ## As if </noscript>
5135                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5136                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5137                                  text => $token->{tag_name}, token => $token);
5138                                
5139                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5140                ## As if </head>                ## As if </head>
# Line 3540  sub _tree_construction_main ($) { Line 5142  sub _tree_construction_main ($) {
5142    
5143                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5144              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5145                  !!!cp ('t147');
5146                ## As if </head>                ## As if </head>
5147                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5148    
5149                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5150              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5151                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
5152                  !!!cp ('t148');
5153                  !!!parse-error (type => 'unmatched end tag',
5154                                  text => $token->{tag_name}, token => $token);
5155                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5156                !!!next-token;                !!!next-token;
5157                redo B;                next B;
5158                } else {
5159                  !!!cp ('t149');
5160              }              }
5161    
5162              ## "after head" insertion mode              ## "after head" insertion mode
5163              ## As if <body>              ## As if <body>
5164              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5165              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5166              ## reprocess              ## reprocess
5167              redo B;              next B;
5168            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5169              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5170            }            !!!cp ('t149.1');
5171    
5172              ## NOTE: As if <head>
5173              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5174              $self->{open_elements}->[-1]->[0]->append_child
5175                  ($self->{head_element});
5176              #push @{$self->{open_elements}},
5177              #    [$self->{head_element}, $el_category->{head}];
5178              #$self->{insertion_mode} = IN_HEAD_IM;
5179              ## NOTE: Reprocess.
5180    
5181              ## NOTE: As if </head>
5182              #pop @{$self->{open_elements}};
5183              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5184              ## NOTE: Reprocess.
5185              
5186              #
5187            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5188              !!!cp ('t149.2');
5189    
5190              ## NOTE: As if </head>
5191              pop @{$self->{open_elements}};
5192              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5193              ## NOTE: Reprocess.
5194    
5195              #
5196            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5197              !!!cp ('t149.3');
5198    
5199              !!!parse-error (type => 'in noscript:#eof', token => $token);
5200    
5201              ## As if </noscript>
5202              pop @{$self->{open_elements}};
5203              #$self->{insertion_mode} = IN_HEAD_IM;
5204              ## NOTE: Reprocess.
5205    
5206              ## NOTE: As if </head>
5207              pop @{$self->{open_elements}};
5208              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5209              ## NOTE: Reprocess.
5210    
5211              #
5212            } else {
5213              !!!cp ('t149.4');
5214              #
5215            }
5216    
5217            ## ISSUE: An issue in the spec.          ## NOTE: As if <body>
5218            !!!insert-element ('body',, $token);
5219            $self->{insertion_mode} = IN_BODY_IM;
5220            ## NOTE: Reprocess.
5221            next B;
5222          } else {
5223            die "$0: $token->{type}: Unknown token type";
5224          }
5225      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5226            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5227                !!!cp ('t150');
5228              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5229              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5230                            
5231              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5232    
5233              !!!next-token;              !!!next-token;
5234              redo B;              next B;
5235            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5236              if ({              if ({
5237                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3578  sub _tree_construction_main ($) { Line 5239  sub _tree_construction_main ($) {
5239                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5240                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5241                  ## have an element in table scope                  ## have an element in table scope
5242                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5243                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5244                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5245                      $tn = $node->[1];                      !!!cp ('t151');
5246                      last INSCOPE;  
5247                    } elsif ({                      ## Close the cell
5248                              table => 1, html => 1,                      !!!back-token; # <x>
5249                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5250                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5251                    }                                line => $token->{line},
5252                  } # INSCOPE                                column => $token->{column}};
5253                    unless (defined $tn) {                      next B;
5254                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5255                      ## Ignore the token                      !!!cp ('t152');
5256                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5257                      redo B;                      last;
5258                    }                    }
5259                                    }
5260                  ## Close the cell  
5261                  !!!back-token; # <?>                  !!!cp ('t153');
5262                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
5263                  redo B;                      text => $token->{tag_name}, token => $token);
5264                    ## Ignore the token
5265                    !!!nack ('t153.1');
5266                    !!!next-token;
5267                    next B;
5268                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5269                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5270                                    token => $token);
5271                                    
5272                  ## As if </caption>                  ## NOTE: As if </caption>.
5273                  ## have a table element in table scope                  ## have a table element in table scope
5274                  my $i;                  my $i;
5275                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5276                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5277                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5278                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5279                      last INSCOPE;                        !!!cp ('t155');
5280                    } elsif ({                        $i = $_;
5281                              table => 1, html => 1,                        last INSCOPE;
5282                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5283                      last INSCOPE;                        !!!cp ('t156');
5284                          last;
5285                        }
5286                    }                    }
5287    
5288                      !!!cp ('t157');
5289                      !!!parse-error (type => 'start tag not allowed',
5290                                      text => $token->{tag_name}, token => $token);
5291                      ## Ignore the token
5292                      !!!nack ('t157.1');
5293                      !!!next-token;
5294                      next B;
5295                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5296                                    
5297                  ## generate implied end tags                  ## generate implied end tags
5298                  if ({                  while ($self->{open_elements}->[-1]->[1]
5299                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5300                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5301                       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;  
5302                  }                  }
5303    
5304                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5305                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5306                      !!!parse-error (type => 'not closed',
5307                                      text => $self->{open_elements}->[-1]->[0]
5308                                          ->manakai_local_name,
5309                                      token => $token);
5310                    } else {
5311                      !!!cp ('t160');
5312                  }                  }
5313                                    
5314                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3650  sub _tree_construction_main ($) { Line 5318  sub _tree_construction_main ($) {
5318                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5319                                    
5320                  ## reprocess                  ## reprocess
5321                  redo B;                  !!!ack-later;
5322                    next B;
5323                } else {                } else {
5324                    !!!cp ('t161');
5325                  #                  #
5326                }                }
5327              } else {              } else {
5328                  !!!cp ('t162');
5329                #                #
5330              }              }
5331            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3664  sub _tree_construction_main ($) { Line 5335  sub _tree_construction_main ($) {
5335                  my $i;                  my $i;
5336                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5337                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5338                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5339                        !!!cp ('t163');
5340                      $i = $_;                      $i = $_;
5341                      last INSCOPE;                      last INSCOPE;
5342                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5343                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5344                      last INSCOPE;                      last INSCOPE;
5345                    }                    }
5346                  } # INSCOPE                  } # INSCOPE
5347                    unless (defined $i) {                    unless (defined $i) {
5348                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5349                        !!!parse-error (type => 'unmatched end tag',
5350                                        text => $token->{tag_name},
5351                                        token => $token);
5352                      ## Ignore the token                      ## Ignore the token
5353                      !!!next-token;                      !!!next-token;
5354                      redo B;                      next B;
5355                    }                    }
5356                                    
5357                  ## generate implied end tags                  ## generate implied end tags
5358                  if ({                  while ($self->{open_elements}->[-1]->[1]
5359                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5360                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5361                       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;  
5362                  }                  }
5363                    
5364                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5365                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5366                      !!!cp ('t167');
5367                      !!!parse-error (type => 'not closed',
5368                                      text => $self->{open_elements}->[-1]->[0]
5369                                          ->manakai_local_name,
5370                                      token => $token);
5371                    } else {
5372                      !!!cp ('t168');
5373                  }                  }
5374                                    
5375                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3705  sub _tree_construction_main ($) { Line 5379  sub _tree_construction_main ($) {
5379                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5380                                    
5381                  !!!next-token;                  !!!next-token;
5382                  redo B;                  next B;
5383                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5384                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5385                    !!!parse-error (type => 'unmatched end tag',
5386                                    text => $token->{tag_name}, token => $token);
5387                  ## Ignore the token                  ## Ignore the token
5388                  !!!next-token;                  !!!next-token;
5389                  redo B;                  next B;
5390                } else {                } else {
5391                    !!!cp ('t170');
5392                  #                  #
5393                }                }
5394              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5395                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5396                  ## have a table element in table scope                  ## have a table element in table scope
5397                  my $i;                  my $i;
5398                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5399                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5400                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5401                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5402                      last INSCOPE;                        !!!cp ('t171');
5403                    } elsif ({                        $i = $_;
5404                              table => 1, html => 1,                        last INSCOPE;
5405                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5406                      last INSCOPE;                        !!!cp ('t172');
5407                          last;
5408                        }
5409                    }                    }
5410    
5411                      !!!cp ('t173');
5412                      !!!parse-error (type => 'unmatched end tag',
5413                                      text => $token->{tag_name}, token => $token);
5414                      ## Ignore the token
5415                      !!!next-token;
5416                      next B;
5417                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5418                                    
5419                  ## generate implied end tags                  ## generate implied end tags
5420                  if ({                  while ($self->{open_elements}->[-1]->[1]
5421                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5422                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5423                       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;  
5424                  }                  }
5425                                    
5426                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5427                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5428                      !!!parse-error (type => 'not closed',
5429                                      text => $self->{open_elements}->[-1]->[0]
5430                                          ->manakai_local_name,
5431                                      token => $token);
5432                    } else {
5433                      !!!cp ('t176');
5434                  }                  }
5435                                    
5436                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3759  sub _tree_construction_main ($) { Line 5440  sub _tree_construction_main ($) {
5440                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5441                                    
5442                  !!!next-token;                  !!!next-token;
5443                  redo B;                  next B;
5444                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5445                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5446                    !!!parse-error (type => 'unmatched end tag',
5447                                    text => $token->{tag_name}, token => $token);
5448                  ## Ignore the token                  ## Ignore the token
5449                  !!!next-token;                  !!!next-token;
5450                  redo B;                  next B;
5451                } else {                } else {
5452                    !!!cp ('t178');
5453                  #                  #
5454                }                }
5455              } elsif ({              } elsif ({
# Line 3776  sub _tree_construction_main ($) { Line 5460  sub _tree_construction_main ($) {
5460                ## have an element in table scope                ## have an element in table scope
5461                my $i;                my $i;
5462                my $tn;                my $tn;
5463                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5464                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5465                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5466                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5467                    last INSCOPE;                      !!!cp ('t179');
5468                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5469                    $tn = $node->[1];  
5470                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5471                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5472                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5473                            table => 1, html => 1,                                line => $token->{line},
5474                           }->{$node->[1]}) {                                column => $token->{column}};
5475                    last INSCOPE;                      next B;
5476                      } elsif ($node->[1] & TABLE_CELL_EL) {
5477                        !!!cp ('t180');
5478                        $tn = $node->[0]->manakai_local_name;
5479                        ## NOTE: There is exactly one |td| or |th| element
5480                        ## in scope in the stack of open elements by definition.
5481                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5482                        ## ISSUE: Can this be reached?
5483                        !!!cp ('t181');
5484                        last;
5485                      }
5486                  }                  }
5487                } # INSCOPE  
5488                unless (defined $i) {                  !!!cp ('t182');
5489                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5490                        text => $token->{tag_name}, token => $token);
5491                  ## Ignore the token                  ## Ignore the token
5492                  !!!next-token;                  !!!next-token;
5493                  redo B;                  next B;
5494                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5495              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5496                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5497                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5498                                  token => $token);
5499    
5500                ## As if </caption>                ## As if </caption>
5501                ## have a table element in table scope                ## have a table element in table scope
5502                my $i;                my $i;
5503                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5504                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5505                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5506                      !!!cp ('t184');
5507                    $i = $_;                    $i = $_;
5508                    last INSCOPE;                    last INSCOPE;
5509                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5510                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5511                    last INSCOPE;                    last INSCOPE;
5512                  }                  }
5513                } # INSCOPE                } # INSCOPE
5514                unless (defined $i) {                unless (defined $i) {
5515                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5516                    !!!parse-error (type => 'unmatched end tag',
5517                                    text => 'caption', token => $token);
5518                  ## Ignore the token                  ## Ignore the token
5519                  !!!next-token;                  !!!next-token;
5520                  redo B;                  next B;
5521                }                }
5522                                
5523                ## generate implied end tags                ## generate implied end tags
5524                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5525                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5526                     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;  
5527                }                }
5528    
5529                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5530                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5531                    !!!parse-error (type => 'not closed',
5532                                    text => $self->{open_elements}->[-1]->[0]
5533                                        ->manakai_local_name,
5534                                    token => $token);
5535                  } else {
5536                    !!!cp ('t189');
5537                }                }
5538    
5539                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3852  sub _tree_construction_main ($) { Line 5543  sub _tree_construction_main ($) {
5543                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5544    
5545                ## reprocess                ## reprocess
5546                redo B;                next B;
5547              } elsif ({              } elsif ({
5548                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5549                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5550                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5551                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
5552                    !!!parse-error (type => 'unmatched end tag',
5553                                    text => $token->{tag_name}, token => $token);
5554                  ## Ignore the token                  ## Ignore the token
5555                  !!!next-token;                  !!!next-token;
5556                  redo B;                  next B;
5557                } else {                } else {
5558                    !!!cp ('t191');
5559                  #                  #
5560                }                }
5561              } elsif ({              } elsif ({
# Line 3869  sub _tree_construction_main ($) { Line 5563  sub _tree_construction_main ($) {
5563                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5564                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5565                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5566                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5567                  !!!parse-error (type => 'unmatched end tag',
5568                                  text => $token->{tag_name}, token => $token);
5569                ## Ignore the token                ## Ignore the token
5570                !!!next-token;                !!!next-token;
5571                redo B;                next B;
5572              } else {              } else {
5573                  !!!cp ('t193');
5574                #                #
5575              }              }
5576          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5577            for my $entry (@{$self->{open_elements}}) {
5578              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5579                !!!cp ('t75');
5580                !!!parse-error (type => 'in body:#eof', token => $token);
5581                last;
5582              }
5583            }
5584    
5585            ## Stop parsing.
5586            last B;
5587        } else {        } else {
5588          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5589        }        }
# Line 3884  sub _tree_construction_main ($) { Line 5592  sub _tree_construction_main ($) {
5592        #        #
5593      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5594        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5595              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5596                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5597              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5598                                
5599                unless (length $token->{data}) {            unless (length $token->{data}) {
5600                  !!!next-token;              !!!cp ('t194');
5601                  redo B;              !!!next-token;
5602                }              next B;
5603              }            } else {
5604                !!!cp ('t195');
5605              }
5606            }
5607    
5608              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5609    
5610              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5611              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5612                            
5613              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5614                   table => 1, tbody => 1, tfoot => 1,            # MUST
5615                   thead => 1, tr => 1,            my $foster_parent_element;
5616                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5617                # MUST            my $prev_sibling;
5618                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5619                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5620                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5621                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5622                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5623                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5624                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5625                      $foster_parent_element = $parent;                  $prev_sibling = $next_sibling->previous_sibling;
5626                      $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});  
5627                } else {                } else {
5628                  $foster_parent_element->insert_before                  !!!cp ('t197');
5629                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5630                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5631                    #
5632                }                }
5633              } else {                last OE;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5634              }              }
5635              } # OE
5636              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5637              $prev_sibling = $foster_parent_element->last_child
5638                  unless defined $foster_parent_element;
5639              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5640              if (defined $prev_sibling and
5641                  $prev_sibling->node_type == 3) {
5642                !!!cp ('t198');
5643                $prev_sibling->manakai_append_text ($token->{data});
5644              } else {
5645                !!!cp ('t199');
5646                $foster_parent_element->insert_before
5647                    ($self->{document}->create_text_node ($token->{data}),
5648                     $next_sibling);
5649              }
5650              $open_tables->[-1]->[1] = 1; # tainted
5651              $open_tables->[-1]->[2] = 1; # ~node inserted
5652            } else {
5653              ## NOTE: Fragment case or in a foster parent'ed element
5654              ## (e.g. |<table><span>a|).  In fragment case, whether the
5655              ## character is appended to existing node or a new node is
5656              ## created is irrelevant, since the foster parent'ed nodes
5657              ## are discarded and fragment parsing does not invoke any
5658              ## script.
5659              !!!cp ('t200');
5660              $self->{open_elements}->[-1]->[0]->manakai_append_text
5661                  ($token->{data});
5662            }
5663                            
5664              !!!next-token;          !!!next-token;
5665              redo B;          next B;
5666        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5667              if ({          if ({
5668                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5669                   th => 1, td => 1,               th => 1, td => 1,
5670                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5671                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5672                  ## Clear back to table context              ## Clear back to table context
5673                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5674                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5675                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5676                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5677                  }              }
5678                                
5679                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5680                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5681                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5682                }            }
5683              
5684                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5685                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5686                    !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t202');
5687                  }                !!!parse-error (type => 'missing start tag:tr', token => $token);
5688                }
5689                                    
5690                  ## Clear back to table body context              ## Clear back to table body context
5691                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5692                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5693                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5694                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## ISSUE: Can this case be reached?
5695                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5696                  }              }
5697                                    
5698                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5699                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5700                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t204');
5701                    !!!next-token;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5702                    redo B;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5703                  } else {                !!!nack ('t204');
5704                    !!!insert-element ('tr');                !!!next-token;
5705                    ## reprocess in the "in row" insertion mode                next B;
5706                  }              } else {
5707                }                !!!cp ('t205');
5708                  !!!insert-element ('tr',, $token);
5709                  ## reprocess in the "in row" insertion mode
5710                }
5711              } else {
5712                !!!cp ('t206');
5713              }
5714    
5715                ## Clear back to table row context                ## Clear back to table row context
5716                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5717                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5718                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5719                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5720                }                }
5721                                
5722                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5723                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5724              $self->{insertion_mode} = IN_CELL_IM;
5725    
5726                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5727                                
5728              !!!nack ('t207.1');
5729              !!!next-token;
5730              next B;
5731            } elsif ({
5732                      caption => 1, col => 1, colgroup => 1,
5733                      tbody => 1, tfoot => 1, thead => 1,
5734                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5735                     }->{$token->{tag_name}}) {
5736              if ($self->{insertion_mode} == IN_ROW_IM) {
5737                ## As if </tr>
5738                ## have an element in table scope
5739                my $i;
5740                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5741                  my $node = $self->{open_elements}->[$_];
5742                  if ($node->[1] & TABLE_ROW_EL) {
5743                    !!!cp ('t208');
5744                    $i = $_;
5745                    last INSCOPE;
5746                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5747                    !!!cp ('t209');
5748                    last INSCOPE;
5749                  }
5750                } # INSCOPE
5751                unless (defined $i) {
5752                  !!!cp ('t210');
5753                  ## TODO: This type is wrong.
5754                  !!!parse-error (type => 'unmacthed end tag',
5755                                  text => $token->{tag_name}, token => $token);
5756                  ## Ignore the token
5757                  !!!nack ('t210.1');
5758                !!!next-token;                !!!next-token;
5759                redo B;                next B;
5760              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] eq 'tr') {  
                     $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;  
                 }  
5761                                    
5762                  ## Clear back to table row context                  ## Clear back to table row context
5763                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5764                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5765                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
5766                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
5767                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5768                  }                  }
5769                                    
5770                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
5771                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5772                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5773                      !!!cp ('t212');
5774                    ## reprocess                    ## reprocess
5775                    redo B;                    !!!ack-later;
5776                      next B;
5777                  } else {                  } else {
5778                      !!!cp ('t213');
5779                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
5780                  }                  }
5781                }                }
# Line 4047  sub _tree_construction_main ($) { Line 5785  sub _tree_construction_main ($) {
5785                  my $i;                  my $i;
5786                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5787                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5788                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
5789                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
5790                      $i = $_;                      $i = $_;
5791                      last INSCOPE;                      last INSCOPE;
5792                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5793                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
5794                      last INSCOPE;                      last INSCOPE;
5795                    }                    }
5796                  } # INSCOPE                  } # INSCOPE
5797                  unless (defined $i) {                  unless (defined $i) {
5798                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
5799    ## TODO: This erorr type is wrong.
5800                      !!!parse-error (type => 'unmatched end tag',
5801                                      text => $token->{tag_name}, token => $token);
5802                    ## Ignore the token                    ## Ignore the token
5803                      !!!nack ('t216.1');
5804                    !!!next-token;                    !!!next-token;
5805                    redo B;                    next B;
5806                  }                  }
5807    
5808                  ## Clear back to table body context                  ## Clear back to table body context
5809                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5810                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5811                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5812                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5813                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5814                  }                  }
5815                                    
# Line 4083  sub _tree_construction_main ($) { Line 5823  sub _tree_construction_main ($) {
5823                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5824                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5825                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
5826                  } else {
5827                    !!!cp ('t218');
5828                }                }
5829    
5830                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5831                  ## Clear back to table context              ## Clear back to table context
5832                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5833                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5834                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t219');
5835                    pop @{$self->{open_elements}};                ## ISSUE: Can this state be reached?
5836                  }                pop @{$self->{open_elements}};
5837                                }
5838                  !!!insert-element ('colgroup');              
5839                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              !!!insert-element ('colgroup',, $token);
5840                  ## reprocess              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5841                  redo B;              ## reprocess
5842                } elsif ({              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5843                          caption => 1,              !!!ack-later;
5844                          colgroup => 1,              next B;
5845                          tbody => 1, tfoot => 1, thead => 1,            } elsif ({
5846                         }->{$token->{tag_name}}) {                      caption => 1,
5847                  ## Clear back to table context                      colgroup => 1,
5848                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                      tbody => 1, tfoot => 1, thead => 1,
5849                         $self->{open_elements}->[-1]->[1] ne 'html') {                     }->{$token->{tag_name}}) {
5850                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## Clear back to table context
5851                    while (not ($self->{open_elements}->[-1]->[1]
5852                                    & TABLE_SCOPING_EL)) {
5853                      !!!cp ('t220');
5854                      ## ISSUE: Can this state be reached?
5855                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5856                  }                  }
5857                                    
5858                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5859                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5860                                    
5861                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5862                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5863                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5864                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5865                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5866                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5867                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5868                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5869                  !!!next-token;                                        }->{$token->{tag_name}};
5870                  redo B;              !!!next-token;
5871                } else {              !!!nack ('t220.1');
5872                  die "$0: in table: <>: $token->{tag_name}";              next B;
5873                }            } else {
5874                die "$0: in table: <>: $token->{tag_name}";
5875              }
5876              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5877                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5878                                  text => $self->{open_elements}->[-1]->[0]
5879                                      ->manakai_local_name,
5880                                  token => $token);
5881    
5882                ## As if </table>                ## As if </table>
5883                ## have a table element in table scope                ## have a table element in table scope
5884                my $i;                my $i;
5885                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5886                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5887                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5888                      !!!cp ('t221');
5889                    $i = $_;                    $i = $_;
5890                    last INSCOPE;                    last INSCOPE;
5891                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5892                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5893                    last INSCOPE;                    last INSCOPE;
5894                  }                  }
5895                } # INSCOPE                } # INSCOPE
5896                unless (defined $i) {                unless (defined $i) {
5897                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5898    ## TODO: The following is wrong, maybe.
5899                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5900                                    token => $token);
5901                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5902                    !!!nack ('t223.1');
5903                  !!!next-token;                  !!!next-token;
5904                  redo B;                  next B;
5905                }                }
5906                                
5907    ## TODO: Followings are removed from the latest spec.
5908                ## generate implied end tags                ## generate implied end tags
5909                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5910                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5911                     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;  
5912                }                }
5913    
5914                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5915                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5916                    ## NOTE: |<table><tr><table>|
5917                    !!!parse-error (type => 'not closed',
5918                                    text => $self->{open_elements}->[-1]->[0]
5919                                        ->manakai_local_name,
5920                                    token => $token);
5921                  } else {
5922                    !!!cp ('t226');
5923                }                }
5924    
5925                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5926                  pop @{$open_tables};
5927    
5928                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5929    
5930                ## reprocess            ## reprocess
5931                redo B;            !!!ack-later;
5932          } else {            next B;
5933            !!!parse-error (type => 'in table:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'style') {
5934              if (not $open_tables->[-1]->[1]) { # tainted
5935                !!!cp ('t227.8');
5936                ## NOTE: This is a "as if in head" code clone.
5937                $parse_rcdata->(CDATA_CONTENT_MODEL);
5938                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5939                next B;
5940              } else {
5941                !!!cp ('t227.7');
5942                #
5943              }
5944            } elsif ($token->{tag_name} eq 'script') {
5945              if (not $open_tables->[-1]->[1]) { # tainted
5946                !!!cp ('t227.6');
5947                ## NOTE: This is a "as if in head" code clone.
5948                $script_start_tag->();
5949                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950                next B;
5951              } else {
5952                !!!cp ('t227.5');
5953                #
5954              }
5955            } elsif ($token->{tag_name} eq 'input') {
5956              if (not $open_tables->[-1]->[1]) { # tainted
5957                if ($token->{attributes}->{type}) { ## TODO: case
5958                  my $type = lc $token->{attributes}->{type}->{value};
5959                  if ($type eq 'hidden') {
5960                    !!!cp ('t227.3');
5961                    !!!parse-error (type => 'in table',
5962                                    text => $token->{tag_name}, token => $token);
5963    
5964            $insert = $insert_to_foster;                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5965                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5966    
5967                    ## TODO: form element pointer
5968    
5969                    pop @{$self->{open_elements}};
5970    
5971                    !!!next-token;
5972                    !!!ack ('t227.2.1');
5973                    next B;
5974                  } else {
5975                    !!!cp ('t227.2');
5976                    #
5977                  }
5978                } else {
5979                  !!!cp ('t227.1');
5980                  #
5981                }
5982              } else {
5983                !!!cp ('t227.4');
5984                #
5985              }
5986            } else {
5987              !!!cp ('t227');
5988            #            #
5989          }          }
5990    
5991            !!!parse-error (type => 'in table', text => $token->{tag_name},
5992                            token => $token);
5993    
5994            $insert = $insert_to_foster;
5995            #
5996        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
5997              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
5998                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 4186  sub _tree_construction_main ($) { Line 6000  sub _tree_construction_main ($) {
6000                my $i;                my $i;
6001                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6002                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6003                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_ROW_EL) {
6004                      !!!cp ('t228');
6005                    $i = $_;                    $i = $_;
6006                    last INSCOPE;                    last INSCOPE;
6007                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6008                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
6009                    last INSCOPE;                    last INSCOPE;
6010                  }                  }
6011                } # INSCOPE                } # INSCOPE
6012                unless (defined $i) {                unless (defined $i) {
6013                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
6014                    !!!parse-error (type => 'unmatched end tag',
6015                                    text => $token->{tag_name}, token => $token);
6016                  ## Ignore the token                  ## Ignore the token
6017                    !!!nack ('t230.1');
6018                  !!!next-token;                  !!!next-token;
6019                  redo B;                  next B;
6020                  } else {
6021                    !!!cp ('t232');
6022                }                }
6023    
6024                ## Clear back to table row context                ## Clear back to table row context
6025                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6026                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6027                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6028                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6029                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6030                }                }
6031    
6032                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6033                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6034                !!!next-token;                !!!next-token;
6035                redo B;                !!!nack ('t231.1');
6036                  next B;
6037              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6038                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6039                  ## As if </tr>                  ## As if </tr>
# Line 4221  sub _tree_construction_main ($) { Line 6041  sub _tree_construction_main ($) {
6041                  my $i;                  my $i;
6042                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6043                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6044                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6045                        !!!cp ('t233');
6046                      $i = $_;                      $i = $_;
6047                      last INSCOPE;                      last INSCOPE;
6048                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6049                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
6050                      last INSCOPE;                      last INSCOPE;
6051                    }                    }
6052                  } # INSCOPE                  } # INSCOPE
6053                  unless (defined $i) {                  unless (defined $i) {
6054                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
6055    ## TODO: The following is wrong.
6056                      !!!parse-error (type => 'unmatched end tag',
6057                                      text => $token->{type}, token => $token);
6058                    ## Ignore the token                    ## Ignore the token
6059                      !!!nack ('t236.1');
6060                    !!!next-token;                    !!!next-token;
6061                    redo B;                    next B;
6062                  }                  }
6063                                    
6064                  ## Clear back to table row context                  ## Clear back to table row context
6065                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6066                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6067                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
6068                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6069                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6070                  }                  }
6071                                    
# Line 4255  sub _tree_construction_main ($) { Line 6079  sub _tree_construction_main ($) {
6079                  my $i;                  my $i;
6080                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6081                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6082                    if ({                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6083                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
6084                      $i = $_;                      $i = $_;
6085                      last INSCOPE;                      last INSCOPE;
6086                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6087                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
6088                      last INSCOPE;                      last INSCOPE;
6089                    }                    }
6090                  } # INSCOPE                  } # INSCOPE
6091                  unless (defined $i) {                  unless (defined $i) {
6092                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
6093                      !!!parse-error (type => 'unmatched end tag',
6094                                      text => $token->{tag_name}, token => $token);
6095                    ## Ignore the token                    ## Ignore the token
6096                      !!!nack ('t239.1');
6097                    !!!next-token;                    !!!next-token;
6098                    redo B;                    next B;
6099                  }                  }
6100                                    
6101                  ## Clear back to table body context                  ## Clear back to table body context
6102                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6103                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
6104                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6105                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6106                  }                  }
6107                                    
# Line 4293  sub _tree_construction_main ($) { Line 6117  sub _tree_construction_main ($) {
6117                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6118                }                }
6119    
6120                  ## NOTE: </table> in the "in table" insertion mode.
6121                  ## When you edit the code fragment below, please ensure that
6122                  ## the code for <table> in the "in table" insertion mode
6123                  ## is synced with it.
6124    
6125                ## have a table element in table scope                ## have a table element in table scope
6126                my $i;                my $i;
6127                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6128                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6129                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6130                      !!!cp ('t241');
6131                    $i = $_;                    $i = $_;
6132                    last INSCOPE;                    last INSCOPE;
6133                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6134                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6135                    last INSCOPE;                    last INSCOPE;
6136                  }                  }
6137                } # INSCOPE                } # INSCOPE
6138                unless (defined $i) {                unless (defined $i) {
6139                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6140                    !!!parse-error (type => 'unmatched end tag',
6141                                    text => $token->{tag_name}, token => $token);
6142                  ## Ignore the token                  ## Ignore the token
6143                    !!!nack ('t243.1');
6144                  !!!next-token;                  !!!next-token;
6145                  redo B;                  next B;
               }  
   
               ## 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]);  
6146                }                }
6147                                    
6148                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6149                  pop @{$open_tables};
6150                                
6151                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6152                                
6153                !!!next-token;                !!!next-token;
6154                redo B;                next B;
6155              } elsif ({              } elsif ({
6156                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6157                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4344  sub _tree_construction_main ($) { Line 6161  sub _tree_construction_main ($) {
6161                  my $i;                  my $i;
6162                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6163                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6164                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6165                        !!!cp ('t247');
6166                      $i = $_;                      $i = $_;
6167                      last INSCOPE;                      last INSCOPE;
6168                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6169                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
6170                      last INSCOPE;                      last INSCOPE;
6171                    }                    }
6172                  } # INSCOPE                  } # INSCOPE
6173                    unless (defined $i) {                    unless (defined $i) {
6174                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
6175                        !!!parse-error (type => 'unmatched end tag',
6176                                        text => $token->{tag_name}, token => $token);
6177                      ## Ignore the token                      ## Ignore the token
6178                        !!!nack ('t249.1');
6179                      !!!next-token;                      !!!next-token;
6180                      redo B;                      next B;
6181                    }                    }
6182                                    
6183                  ## As if </tr>                  ## As if </tr>
# Line 4365  sub _tree_construction_main ($) { Line 6185  sub _tree_construction_main ($) {
6185                  my $i;                  my $i;
6186                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6187                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6188                    if ($node->[1] eq 'tr') {                    if ($node->[1] & TABLE_ROW_EL) {
6189                        !!!cp ('t250');
6190                      $i = $_;                      $i = $_;
6191                      last INSCOPE;                      last INSCOPE;
6192                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6193                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
6194                      last INSCOPE;                      last INSCOPE;
6195                    }                    }
6196                  } # INSCOPE                  } # INSCOPE
6197                    unless (defined $i) {                    unless (defined $i) {
6198                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
6199                        !!!parse-error (type => 'unmatched end tag',
6200                                        text => 'tr', token => $token);
6201                      ## Ignore the token                      ## Ignore the token
6202                        !!!nack ('t252.1');
6203                      !!!next-token;                      !!!next-token;
6204                      redo B;                      next B;
6205                    }                    }
6206                                    
6207                  ## Clear back to table row context                  ## Clear back to table row context
6208                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6209                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6210                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
6211                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6212                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6213                  }                  }
6214                                    
# Line 4398  sub _tree_construction_main ($) { Line 6221  sub _tree_construction_main ($) {
6221                my $i;                my $i;
6222                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6223                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6224                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6225                      !!!cp ('t254');
6226                    $i = $_;                    $i = $_;
6227                    last INSCOPE;                    last INSCOPE;
6228                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6229                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6230                    last INSCOPE;                    last INSCOPE;
6231                  }                  }
6232                } # INSCOPE                } # INSCOPE
6233                unless (defined $i) {                unless (defined $i) {
6234                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
6235                    !!!parse-error (type => 'unmatched end tag',
6236                                    text => $token->{tag_name}, token => $token);
6237                  ## Ignore the token                  ## Ignore the token
6238                    !!!nack ('t256.1');
6239                  !!!next-token;                  !!!next-token;
6240                  redo B;                  next B;
6241                }                }
6242    
6243                ## Clear back to table body context                ## Clear back to table body context
6244                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6245                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6246                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6247                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6248                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6249                }                }
6250    
6251                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6252                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6253                  !!!nack ('t257.1');
6254                !!!next-token;                !!!next-token;
6255                redo B;                next B;
6256              } elsif ({              } elsif ({
6257                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6258                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6259                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6260                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6261                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6262                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6263                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6264                !!!next-token;                            text => $token->{tag_name}, token => $token);
6265                redo B;            ## Ignore the token
6266          } else {            !!!nack ('t258.1');
6267            !!!parse-error (type => 'in table:/'.$token->{tag_name});             !!!next-token;
6268              next B;
6269            } else {
6270              !!!cp ('t259');
6271              !!!parse-error (type => 'in table:/',
6272                              text => $token->{tag_name}, token => $token);
6273    
6274            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6275            #            #
6276          }          }
6277          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6278            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6279                    @{$self->{open_elements}} == 1) { # redundant, maybe
6280              !!!parse-error (type => 'in body:#eof', token => $token);
6281              !!!cp ('t259.1');
6282              #
6283            } else {
6284              !!!cp ('t259.2');
6285              #
6286            }
6287    
6288            ## Stop parsing
6289            last B;
6290        } else {        } else {
6291          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6292        }        }
6293      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6294            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6295              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6296                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6297                unless (length $token->{data}) {                unless (length $token->{data}) {
6298                    !!!cp ('t260');
6299                  !!!next-token;                  !!!next-token;
6300                  redo B;                  next B;
6301                }                }
6302              }              }
6303                            
6304                !!!cp ('t261');
6305              #              #
6306            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6307              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6308                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
6309                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6310                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6311                  !!!ack ('t262.1');
6312                !!!next-token;                !!!next-token;
6313                redo B;                next B;
6314              } else {              } else {
6315                  !!!cp ('t263');
6316                #                #
6317              }              }
6318            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6319              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6320                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6321                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
6322                    !!!parse-error (type => 'unmatched end tag',
6323                                    text => 'colgroup', token => $token);
6324                  ## Ignore the token                  ## Ignore the token
6325                  !!!next-token;                  !!!next-token;
6326                  redo B;                  next B;
6327                } else {                } else {
6328                    !!!cp ('t265');
6329                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6330                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6331                  !!!next-token;                  !!!next-token;
6332                  redo B;                              next B;            
6333                }                }
6334              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6335                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
6336                  !!!parse-error (type => 'unmatched end tag',
6337                                  text => 'col', token => $token);
6338                ## Ignore the token                ## Ignore the token
6339                !!!next-token;                !!!next-token;
6340                redo B;                next B;
6341              } else {              } else {
6342                  !!!cp ('t267');
6343                #                #
6344              }              }
6345            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6346              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6347            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6348              !!!cp ('t270.2');
6349              ## Stop parsing.
6350              last B;
6351            } else {
6352              ## NOTE: As if </colgroup>.
6353              !!!cp ('t270.1');
6354              pop @{$self->{open_elements}}; # colgroup
6355              $self->{insertion_mode} = IN_TABLE_IM;
6356              ## Reprocess.
6357              next B;
6358            }
6359          } else {
6360            die "$0: $token->{type}: Unknown token type";
6361          }
6362    
6363            ## As if </colgroup>            ## As if </colgroup>
6364            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6365              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
6366    ## TODO: Wrong error type?
6367                !!!parse-error (type => 'unmatched end tag',
6368                                text => 'colgroup', token => $token);
6369              ## Ignore the token              ## Ignore the token
6370                !!!nack ('t269.1');
6371              !!!next-token;              !!!next-token;
6372              redo B;              next B;
6373            } else {            } else {
6374                !!!cp ('t270');
6375              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6376              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6377                !!!ack-later;
6378              ## reprocess              ## reprocess
6379              redo B;              next B;
6380            }            }
6381      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6382        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6383            !!!cp ('t271');
6384          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6385          !!!next-token;          !!!next-token;
6386          redo B;          next B;
6387        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6388              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6389                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6390                  ## As if </option>              !!!cp ('t272');
6391                  pop @{$self->{open_elements}};              ## As if </option>
6392                }              pop @{$self->{open_elements}};
6393              } else {
6394                !!!cp ('t273');
6395              }
6396    
6397                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6398                !!!next-token;            !!!nack ('t273.1');
6399                redo B;            !!!next-token;
6400              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6401                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6402                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6403                  pop @{$self->{open_elements}};              !!!cp ('t274');
6404                }              ## As if </option>
6405                pop @{$self->{open_elements}};
6406              } else {
6407                !!!cp ('t275');
6408              }
6409    
6410                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6411                  ## As if </optgroup>              !!!cp ('t276');
6412                  pop @{$self->{open_elements}};              ## As if </optgroup>
6413                }              pop @{$self->{open_elements}};
6414              } else {
6415                !!!cp ('t277');
6416              }
6417    
6418                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6419                !!!next-token;            !!!nack ('t277.1');
6420                redo B;            !!!next-token;
6421              } elsif ($token->{tag_name} eq 'select') {            next B;
6422                !!!parse-error (type => 'not closed:select');          } elsif ({
6423                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6424                ## have an element in table scope                   }->{$token->{tag_name}} or
6425                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6426                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6427                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6428                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6429                    $i = $_;                     tr => 1, td => 1, th => 1,
6430                    last INSCOPE;                    }->{$token->{tag_name}})) {
6431                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6432                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6433                           }->{$node->[1]}) {                            token => $token);
6434                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6435                  }            ## as if there were </select> (otherwise).
6436                } # INSCOPE            ## have an element in table scope
6437                unless (defined $i) {            my $i;
6438                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6439                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6440                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6441                  redo B;                !!!cp ('t278');
6442                }                $i = $_;
6443                  last INSCOPE;
6444                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6445                  !!!cp ('t279');
6446                  last INSCOPE;
6447                }
6448              } # INSCOPE
6449              unless (defined $i) {
6450                !!!cp ('t280');
6451                !!!parse-error (type => 'unmatched end tag',
6452                                text => 'select', token => $token);
6453                ## Ignore the token
6454                !!!nack ('t280.1');
6455                !!!next-token;
6456                next B;
6457              }
6458                                
6459                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6460              splice @{$self->{open_elements}}, $i;
6461    
6462                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6463    
6464                !!!next-token;            if ($token->{tag_name} eq 'select') {
6465                redo B;              !!!nack ('t281.2');
6466                !!!next-token;
6467                next B;
6468              } else {
6469                !!!cp ('t281.1');
6470                !!!ack-later;
6471                ## Reprocess the token.
6472                next B;
6473              }
6474          } else {          } else {
6475            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!cp ('t282');
6476              !!!parse-error (type => 'in select',
6477                              text => $token->{tag_name}, token => $token);
6478            ## Ignore the token            ## Ignore the token
6479              !!!nack ('t282.1');
6480            !!!next-token;            !!!next-token;
6481            redo B;            next B;
6482          }          }
6483        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6484              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6485                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6486                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6487                  ## As if </option>              !!!cp ('t283');
6488                  splice @{$self->{open_elements}}, -2;              ## As if </option>
6489                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              splice @{$self->{open_elements}}, -2;
6490                  pop @{$self->{open_elements}};            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6491                } else {              !!!cp ('t284');
6492                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              pop @{$self->{open_elements}};
6493                  ## Ignore the token            } else {
6494                }              !!!cp ('t285');
6495                !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6496                redo B;                              text => $token->{tag_name}, token => $token);
6497              } elsif ($token->{tag_name} eq 'option') {              ## Ignore the token
6498                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6499                  pop @{$self->{open_elements}};            !!!nack ('t285.1');
6500                } else {            !!!next-token;
6501                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            next B;
6502                  ## Ignore the token          } elsif ($token->{tag_name} eq 'option') {
6503                }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6504                !!!next-token;              !!!cp ('t286');
6505                redo B;              pop @{$self->{open_elements}};
6506              } elsif ($token->{tag_name} eq 'select') {            } else {
6507                ## have an element in table scope              !!!cp ('t287');
6508                my $i;              !!!parse-error (type => 'unmatched end tag',
6509                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                              text => $token->{tag_name}, token => $token);
6510                  my $node = $self->{open_elements}->[$_];              ## Ignore the token
6511                  if ($node->[1] eq $token->{tag_name}) {            }
6512                    $i = $_;            !!!nack ('t287.1');
6513                    last INSCOPE;            !!!next-token;
6514                  } elsif ({            next B;
6515                            table => 1, html => 1,          } elsif ($token->{tag_name} eq 'select') {
6516                           }->{$node->[1]}) {            ## have an element in table scope
6517                    last INSCOPE;            my $i;
6518                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6519                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6520                unless (defined $i) {              if ($node->[1] & SELECT_EL) {
6521                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t288');
6522                  ## Ignore the token                $i = $_;
6523                  !!!next-token;                last INSCOPE;
6524                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6525                }                !!!cp ('t289');
6526                  last INSCOPE;
6527                }
6528              } # INSCOPE
6529              unless (defined $i) {
6530                !!!cp ('t290');
6531                !!!parse-error (type => 'unmatched end tag',
6532                                text => $token->{tag_name}, token => $token);
6533                ## Ignore the token
6534                !!!nack ('t290.1');
6535                !!!next-token;
6536                next B;
6537              }
6538                                
6539                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6540              splice @{$self->{open_elements}}, $i;
6541    
6542                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6543    
6544                !!!next-token;            !!!nack ('t291.1');
6545                redo B;            !!!next-token;
6546              } elsif ({            next B;
6547                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6548                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6549                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6550                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6551                     }->{$token->{tag_name}}) {
6552    ## TODO: The following is wrong?
6553              !!!parse-error (type => 'unmatched end tag',
6554                              text => $token->{tag_name}, token => $token);
6555                                
6556                ## have an element in table scope            ## have an element in table scope
6557                my $i;            my $i;
6558                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6559                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6560                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6561                    $i = $_;                !!!cp ('t292');
6562                    last INSCOPE;                $i = $_;
6563                  } elsif ({                last INSCOPE;
6564                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6565                           }->{$node->[1]}) {                !!!cp ('t293');
6566                    last INSCOPE;                last INSCOPE;
6567                  }              }
6568                } # INSCOPE            } # INSCOPE
6569                unless (defined $i) {            unless (defined $i) {
6570                  ## Ignore the token              !!!cp ('t294');
6571                  !!!next-token;              ## Ignore the token
6572                  redo B;              !!!nack ('t294.1');
6573                }              !!!next-token;
6574                next B;
6575              }
6576                                
6577                ## As if </select>            ## As if </select>
6578                ## have an element in table scope            ## have an element in table scope
6579                undef $i;            undef $i;
6580                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6581                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6582                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6583                    $i = $_;                !!!cp ('t295');
6584                    last INSCOPE;                $i = $_;
6585                  } elsif ({                last INSCOPE;
6586                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6587                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
6588                    last INSCOPE;                !!!cp ('t296');
6589                  }                last INSCOPE;
6590                } # INSCOPE              }
6591                unless (defined $i) {            } # INSCOPE
6592                  !!!parse-error (type => 'unmatched end tag:select');            unless (defined $i) {
6593                  ## Ignore the </select> token              !!!cp ('t297');
6594                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
6595                  redo B;              !!!parse-error (type => 'unmatched end tag',
6596                }                              text => 'select', token => $token);
6597                ## Ignore the </select> token
6598                !!!nack ('t297.1');
6599                !!!next-token; ## TODO: ok?
6600                next B;
6601              }
6602                                
6603                splice @{$self->{open_elements}}, $i;            !!!cp ('t298');
6604              splice @{$self->{open_elements}}, $i;
6605    
6606                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6607    
6608                ## reprocess            !!!ack-later;
6609                redo B;            ## reprocess
6610              next B;
6611          } else {          } else {
6612            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!cp ('t299');
6613              !!!parse-error (type => 'in select:/',
6614                              text => $token->{tag_name}, token => $token);
6615            ## Ignore the token            ## Ignore the token
6616              !!!nack ('t299.3');
6617            !!!next-token;            !!!next-token;
6618            redo B;            next B;
6619            }
6620          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6621            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6622                    @{$self->{open_elements}} == 1) { # redundant, maybe
6623              !!!cp ('t299.1');
6624              !!!parse-error (type => 'in body:#eof', token => $token);
6625            } else {
6626              !!!cp ('t299.2');
6627          }          }
6628    
6629            ## Stop parsing.
6630            last B;
6631        } else {        } else {
6632          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6633        }        }
6634      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6635        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6636          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6637            my $data = $1;            my $data = $1;
6638            ## As if in body            ## As if in body
6639            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 4687  sub _tree_construction_main ($) { Line 6641  sub _tree_construction_main ($) {
6641            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6642                        
6643            unless (length $token->{data}) {            unless (length $token->{data}) {
6644                !!!cp ('t300');
6645              !!!next-token;              !!!next-token;
6646              redo B;              next B;
6647            }            }
6648          }          }
6649                    
6650          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6651            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
6652              !!!parse-error (type => 'after html:#text', token => $token);
6653            ## Reprocess in the "main" phase, "after body" insertion mode...            #
6654            } else {
6655              !!!cp ('t302');
6656              ## "after body" insertion mode
6657              !!!parse-error (type => 'after body:#text', token => $token);
6658              #
6659          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6660    
6661          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6662          ## reprocess          ## reprocess
6663          redo B;          next B;
6664        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6665          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6666            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
6667                        !!!parse-error (type => 'after html',
6668            ## Reprocess in the "main" phase, "after body" insertion mode...                            text => $token->{tag_name}, token => $token);
6669              #
6670            } else {
6671              !!!cp ('t304');
6672              ## "after body" insertion mode
6673              !!!parse-error (type => 'after body',
6674                              text => $token->{tag_name}, token => $token);
6675              #
6676          }          }
6677    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6678          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6679            !!!ack-later;
6680          ## reprocess          ## reprocess
6681          redo B;          next B;
6682        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6683          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6684            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
6685              !!!parse-error (type => 'after html:/',
6686                              text => $token->{tag_name}, token => $token);
6687                        
6688            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6689            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess.
6690              next B;
6691            } else {
6692              !!!cp ('t306');
6693          }          }
6694    
6695          ## "after body" insertion mode          ## "after body" insertion mode
6696          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6697            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6698              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
6699                !!!parse-error (type => 'unmatched end tag',
6700                                text => 'html', token => $token);
6701              ## Ignore the token              ## Ignore the token
6702              !!!next-token;              !!!next-token;
6703              redo B;              next B;
6704            } else {            } else {
6705                !!!cp ('t308');
6706              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6707              !!!next-token;              !!!next-token;
6708              redo B;              next B;
6709            }            }
6710          } else {          } else {
6711            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
6712              !!!parse-error (type => 'after body:/',
6713                              text => $token->{tag_name}, token => $token);
6714    
6715            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6716            ## reprocess            ## reprocess
6717            redo B;            next B;
6718          }          }
6719          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6720            !!!cp ('t309.2');
6721            ## Stop parsing
6722            last B;
6723        } else {        } else {
6724          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6725        }        }
6726      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6727        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6728          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6729            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6730                        
6731            unless (length $token->{data}) {            unless (length $token->{data}) {
6732                !!!cp ('t310');
6733              !!!next-token;              !!!next-token;
6734              redo B;              next B;
6735            }            }
6736          }          }
6737                    
6738          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6739            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6740              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
6741                !!!parse-error (type => 'in frameset:#text', token => $token);
6742            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6743              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
6744            } else { # "after html frameset"              !!!parse-error (type => 'after frameset:#text', token => $token);
6745              !!!parse-error (type => 'after html:#character');            } else { # "after after frameset"
6746                !!!cp ('t313');
6747              $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');  
6748            }            }
6749                        
6750            ## Ignore the token.            ## Ignore the token.
6751            if (length $token->{data}) {            if (length $token->{data}) {
6752                !!!cp ('t314');
6753              ## reprocess the rest of characters              ## reprocess the rest of characters
6754            } else {            } else {
6755                !!!cp ('t315');
6756              !!!next-token;              !!!next-token;
6757            }            }
6758            redo B;            next B;
6759          }          }
6760                    
6761          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6762        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6763          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6764              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6765            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6766              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6767              !!!nack ('t318.1');
6768            !!!next-token;            !!!next-token;
6769            redo B;            next B;
6770          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6771                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6772            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
6773              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6774            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6775              !!!ack ('t319.1');
6776            !!!next-token;            !!!next-token;
6777            redo B;            next B;
6778          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6779            ## NOTE: As if in body.            !!!cp ('t320');
6780            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6781            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6782              next B;
6783    
6784              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6785              ## has no parse error.
6786          } else {          } else {
6787            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6788              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
6789            } else {              !!!parse-error (type => 'in frameset',
6790              !!!parse-error (type => 'after frameset:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6791              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6792                !!!cp ('t322');
6793                !!!parse-error (type => 'after frameset',
6794                                text => $token->{tag_name}, token => $token);
6795              } else { # "after after frameset"
6796                !!!cp ('t322.2');
6797                !!!parse-error (type => 'after after frameset',
6798                                text => $token->{tag_name}, token => $token);
6799            }            }
6800            ## Ignore the token            ## Ignore the token
6801              !!!nack ('t322.1');
6802            !!!next-token;            !!!next-token;
6803            redo B;            next B;
6804          }          }
6805        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
         if ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {  
           !!!parse-error (type => 'after html:/'.$token->{tag_name});  
   
           $self->{insertion_mode} = AFTER_FRAMESET_IM;  
           ## Process in the "main" phase, "after frameset" insertion mode...  
         }  
   
6806          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6807              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6808            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6809                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6810              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6811                !!!parse-error (type => 'unmatched end tag',
6812                                text => $token->{tag_name}, token => $token);
6813              ## Ignore the token              ## Ignore the token
6814              !!!next-token;              !!!next-token;
6815            } else {            } else {
6816                !!!cp ('t326');
6817              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6818              !!!next-token;              !!!next-token;
6819            }            }
6820    
6821            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6822                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6823                !!!cp ('t327');
6824              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6825              } else {
6826                !!!cp ('t328');
6827            }            }
6828            redo B;            next B;
6829          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6830                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6831              !!!cp ('t329');
6832            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6833            !!!next-token;            !!!next-token;
6834            redo B;            next B;
6835          } else {          } else {
6836            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6837              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
6838            } else {              !!!parse-error (type => 'in frameset:/',
6839              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6840              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6841                !!!cp ('t330.1');
6842                !!!parse-error (type => 'after frameset:/',
6843                                text => $token->{tag_name}, token => $token);
6844              } else { # "after after html"
6845                !!!cp ('t331');
6846                !!!parse-error (type => 'after after frameset:/',
6847                                text => $token->{tag_name}, token => $token);
6848            }            }
6849            ## Ignore the token            ## Ignore the token
6850            !!!next-token;            !!!next-token;
6851            redo B;            next B;
6852          }          }
6853          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6854            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6855                    @{$self->{open_elements}} == 1) { # redundant, maybe
6856              !!!cp ('t331.1');
6857              !!!parse-error (type => 'in body:#eof', token => $token);
6858            } else {
6859              !!!cp ('t331.2');
6860            }
6861            
6862            ## Stop parsing
6863            last B;
6864        } else {        } else {
6865          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6866        }        }
   
       ## ISSUE: An issue in spec here  
6867      } else {      } else {
6868        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6869      }      }
# Line 4866  sub _tree_construction_main ($) { Line 6871  sub _tree_construction_main ($) {
6871      ## "in body" insertion mode      ## "in body" insertion mode
6872      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
6873        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6874            !!!cp ('t332');
6875          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6876          $script_start_tag->($insert);          $script_start_tag->();
6877          redo B;          next B;
6878        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6879            !!!cp ('t333');
6880          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6881          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6882          redo B;          next B;
6883        } elsif ({        } elsif ({
6884                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6885                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6886            !!!cp ('t334');
6887          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6888          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6889          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6890            !!!ack ('t334.1');
6891          !!!next-token;          !!!next-token;
6892          redo B;          next B;
6893        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6894          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
6895          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6896          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6897    
6898          unless ($self->{confident}) {          unless ($self->{confident}) {
6899            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6900                !!!cp ('t335');
6901                ## NOTE: Whether the encoding is supported or not is handled
6902                ## in the {change_encoding} callback.
6903              $self->{change_encoding}              $self->{change_encoding}
6904                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6905                            
6906              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6907                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6908                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6909                                           ->{has_reference});                                           ->{has_reference});
6910            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6911              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6912                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6913                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6914                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6915                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6916                       /x) {
6917                  !!!cp ('t336');
6918                  ## NOTE: Whether the encoding is supported or not is handled
6919                  ## in the {change_encoding} callback.
6920                $self->{change_encoding}                $self->{change_encoding}
6921                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6922                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6923                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6924                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 4912  sub _tree_construction_main ($) { Line 6927  sub _tree_construction_main ($) {
6927            }            }
6928          } else {          } else {
6929            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
6930                !!!cp ('t337');
6931              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6932                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6933                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6934                                           ->{has_reference});                                           ->{has_reference});
6935            }            }
6936            if ($token->{attributes}->{content}) {            if ($token->{attributes}->{content}) {
6937                !!!cp ('t338');
6938              $meta_el->[0]->get_attribute_node_ns (undef, 'content')              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6939                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6940                                       $token->{attributes}->{content}                                       $token->{attributes}->{content}
# Line 4925  sub _tree_construction_main ($) { Line 6942  sub _tree_construction_main ($) {
6942            }            }
6943          }          }
6944    
6945            !!!ack ('t338.1');
6946          !!!next-token;          !!!next-token;
6947          redo B;          next B;
6948        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6949          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
6950          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6951          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6952            if (defined $self->{head_element}) {          next B;
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6953        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6954          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6955                                
6956          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6957              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6958              !!!cp ('t342');
6959            ## Ignore the token            ## Ignore the token
6960          } else {          } else {
6961            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
6962            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
6963              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6964                  !!!cp ('t343');
6965                $body_el->set_attribute_ns                $body_el->set_attribute_ns
6966                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
6967                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
6968              }              }
6969            }            }
6970          }          }
6971            !!!nack ('t343.1');
6972          !!!next-token;          !!!next-token;
6973          redo B;          next B;
6974        } elsif ({        } elsif ({
6975                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6976                  div => 1, dl => 1, fieldset => 1, listing => 1,  
6977                  menu => 1, ol => 1, p => 1, ul => 1,                  ## NOTE: The normal one
6978                  pre => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6979                    center => 1, datagrid => 1, details => 1, dialog => 1,
6980                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6981                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6982                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6983                    section => 1, ul => 1,
6984                    ## NOTE: As normal, but drops leading newline
6985                    pre => 1, listing => 1,
6986                    ## NOTE: As normal, but interacts with the form element pointer
6987                    form => 1,
6988                    
6989                    table => 1,
6990                    hr => 1,
6991                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6992            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6993              !!!cp ('t350');
6994              !!!parse-error (type => 'in form:form', token => $token);
6995              ## Ignore the token
6996              !!!nack ('t350.1');
6997              !!!next-token;
6998              next B;
6999            }
7000    
7001          ## has a p element in scope          ## has a p element in scope
7002          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7003            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7004              !!!back-token;              !!!cp ('t344');
7005              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <form>
7006              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7007            } elsif ({                        line => $token->{line}, column => $token->{column}};
7008                      table => 1, caption => 1, td => 1, th => 1,              next B;
7009                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7010                     }->{$_->[1]}) {              !!!cp ('t345');
7011              last INSCOPE;              last INSCOPE;
7012            }            }
7013          } # INSCOPE          } # INSCOPE
7014                        
7015          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7016          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7017              !!!nack ('t346.1');
7018            !!!next-token;            !!!next-token;
7019            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7020              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
7021              unless (length $token->{data}) {              unless (length $token->{data}) {
7022                  !!!cp ('t346');
7023                !!!next-token;                !!!next-token;
7024                } else {
7025                  !!!cp ('t349');
7026              }              }
7027              } else {
7028                !!!cp ('t348');
7029            }            }
7030          } else {          } elsif ($token->{tag_name} eq 'form') {
7031              !!!cp ('t347.1');
7032              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7033    
7034              !!!nack ('t347.2');
7035            !!!next-token;            !!!next-token;
7036          }          } elsif ($token->{tag_name} eq 'table') {
7037          redo B;            !!!cp ('t382');
7038        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7039          if (defined $self->{form_element}) {            
7040            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
7041            ## Ignore the token  
7042              !!!nack ('t382.1');
7043              !!!next-token;
7044            } elsif ($token->{tag_name} eq 'hr') {
7045              !!!cp ('t386');
7046              pop @{$self->{open_elements}};
7047            
7048              !!!nack ('t386.1');
7049            !!!next-token;            !!!next-token;
           redo B;  
7050          } else {          } else {
7051            ## has a p element in scope            !!!nack ('t347.1');
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!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];  
7052            !!!next-token;            !!!next-token;
           redo B;  
7053          }          }
7054            next B;
7055        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7056          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7057          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7058            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7059              !!!back-token;            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7060              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7061              redo B;            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7062            } elsif ({            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7063                      table => 1, caption => 1, td => 1, th => 1,            ## object (Fx)
7064                      button => 1, marquee => 1, object => 1, html => 1,            ## Generate non-tree (non-conforming)
7065                     }->{$_->[1]}) {            ## basefont (IE7 (where basefont is non-void)), center (IE),
7066              last INSCOPE;            ## form (IE), hn (IE)
7067            }          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7068          } # INSCOPE            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7069                        ## div (Fx, S)
7070          ## Step 1  
7071            my $non_optional;
7072          my $i = -1;          my $i = -1;
7073          my $node = $self->{open_elements}->[$i];  
7074          LI: {          ## 1.
7075            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7076            if ($node->[1] eq 'li') {            if ($node->[1] & LI_EL) {
7077              if ($i != -1) {              ## 2. (a) As if </li>
7078                !!!parse-error (type => 'end tag missing:'.              {
7079                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7080                  #
7081    
7082                  ## Otherwise
7083    
7084                  ## 1. generate implied end tags, except for </li>
7085                  #
7086    
7087                  ## 2. If current node != "li", parse error
7088                  if ($non_optional) {
7089                    !!!parse-error (type => 'not closed',
7090                                    text => $non_optional->[0]->manakai_local_name,
7091                                    token => $token);
7092                    !!!cp ('t355');
7093                  } else {
7094                    !!!cp ('t356');
7095                  }
7096    
7097                  ## 3. Pop
7098                  splice @{$self->{open_elements}}, $i;
7099              }              }
7100              splice @{$self->{open_elements}}, $i;  
7101              last LI;              last; ## 2. (b) goto 5.
7102            }            } elsif (
7103                                 ## NOTE: not "formatting" and not "phrasing"
7104            ## Step 3                     ($node->[1] & SPECIAL_EL or
7105            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7106                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7107                ($special_category->{$node->[1]} or  
7108                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7109                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7110              last LI;                     (not $node->[1] & P_EL)) {
7111                ## 3.
7112                !!!cp ('t357');
7113                last; ## goto 5.
7114              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7115                !!!cp ('t358');
7116                #
7117              } else {
7118                !!!cp ('t359');
7119                $non_optional ||= $node;
7120                #
7121            }            }
7122                        ## 4.
7123            ## Step 4            ## goto 2.
7124            $i--;            $i--;
7125            $node = $self->{open_elements}->[$i];          }
7126            redo LI;  
7127          } # LI          ## 5. (a) has a |p| element in scope
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
7128          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7129            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7130              !!!back-token;              !!!cp ('t353');
7131              $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
7132              redo B;              ## NOTE: |<p><li>|, for example.
7133            } elsif ({  
7134                      table => 1, caption => 1, td => 1, th => 1,              !!!back-token; # <x>
7135                      button => 1, marquee => 1, object => 1, html => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7136                     }->{$_->[1]}) {                        line => $token->{line}, column => $token->{column}};
7137                next B;
7138              } elsif ($_->[1] & SCOPING_EL) {
7139                !!!cp ('t354');
7140              last INSCOPE;              last INSCOPE;
7141            }            }
7142          } # INSCOPE          } # INSCOPE
7143              
7144          ## Step 1          ## 5. (b) insert
7145            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7146            !!!nack ('t359.1');
7147            !!!next-token;
7148            next B;
7149          } elsif ($token->{tag_name} eq 'dt' or
7150                   $token->{tag_name} eq 'dd') {
7151            ## NOTE: As normal, but imply </dt> or </dd> when ...
7152    
7153            my $non_optional;
7154          my $i = -1;          my $i = -1;
7155          my $node = $self->{open_elements}->[$i];  
7156          LI: {          ## 1.
7157            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7158            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7159              if ($i != -1) {              ## 2. (a) As if </li>
7160                !!!parse-error (type => 'end tag missing:'.              {
7161                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7162                  #
7163    
7164                  ## Otherwise
7165    
7166                  ## 1. generate implied end tags, except for </dt> or </dd>
7167                  #
7168    
7169                  ## 2. If current node != "dt"|"dd", parse error
7170                  if ($non_optional) {
7171                    !!!parse-error (type => 'not closed',
7172                                    text => $non_optional->[0]->manakai_local_name,
7173                                    token => $token);
7174                    !!!cp ('t355.1');
7175                  } else {
7176                    !!!cp ('t356.1');
7177                  }
7178    
7179                  ## 3. Pop
7180                  splice @{$self->{open_elements}}, $i;
7181              }              }
7182              splice @{$self->{open_elements}}, $i;  
7183              last LI;              last; ## 2. (b) goto 5.
7184            }            } elsif (
7185                                 ## NOTE: not "formatting" and not "phrasing"
7186            ## Step 3                     ($node->[1] & SPECIAL_EL or
7187            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7188                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7189                ($special_category->{$node->[1]} or  
7190                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_EL) &
7191                $node->[1] ne 'address' and $node->[1] ne 'div') {                     (not $node->[1] & DIV_EL) &
7192              last LI;                     (not $node->[1] & P_EL)) {
7193                ## 3.
7194                !!!cp ('t357.1');
7195                last; ## goto 5.
7196              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7197                !!!cp ('t358.1');
7198                #
7199              } else {
7200                !!!cp ('t359.1');
7201                $non_optional ||= $node;
7202                #
7203            }            }
7204                        ## 4.
7205            ## Step 4            ## goto 2.
7206            $i--;            $i--;
7207            $node = $self->{open_elements}->[$i];          }
7208            redo LI;  
7209          } # 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  
7210          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7211            if ($_->[1] eq 'p') {            if ($_->[1] & P_EL) {
7212              !!!back-token;              !!!cp ('t353.1');
7213              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <x>
7214              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7215            } elsif ({                        line => $token->{line}, column => $token->{column}};
7216                      table => 1, caption => 1, td => 1, th => 1,              next B;
7217                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7218                     }->{$_->[1]}) {              !!!cp ('t354.1');
7219              last INSCOPE;              last INSCOPE;
7220            }            }
7221          } # INSCOPE          } # INSCOPE
7222              
7223          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          ## 5. (b) insert
7224                      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7225          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          !!!nack ('t359.2');
             
7226          !!!next-token;          !!!next-token;
7227          redo B;          next B;
7228        } elsif ({        } elsif ($token->{tag_name} eq 'plaintext') {
7229                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          ## NOTE: As normal, but effectively ends parsing
7230                 }->{$token->{tag_name}}) {  
7231          ## has a p element in scope          ## has a p element in scope
7232          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7233            my $node = $self->{open_elements}->[$_];            if ($_->[1] & P_EL) {
7234            if ($node->[1] eq 'p') {              !!!cp ('t367');
7235              !!!back-token;              !!!back-token; # <plaintext>
7236              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7237              redo B;                        line => $token->{line}, column => $token->{column}};
7238            } elsif ({              next B;
7239                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
7240                      button => 1, marquee => 1, object => 1, html => 1,              !!!cp ('t368');
                    }->{$node->[1]}) {  
7241              last INSCOPE;              last INSCOPE;
7242            }            }
7243          } # INSCOPE          } # INSCOPE
7244                        
7245          ## 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;  
         #}  
7246                        
7247          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7248                        
7249            !!!nack ('t368.1');
7250          !!!next-token;          !!!next-token;
7251          redo B;          next B;
7252        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7253          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7254            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7255            if ($node->[1] eq 'a') {            if ($node->[1] & A_EL) {
7256              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
7257                !!!parse-error (type => 'in a:a', token => $token);
7258                            
7259              !!!back-token;              !!!back-token; # <a>
7260              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7261              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7262                $formatting_end_tag->($token);
7263                            
7264              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7265                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7266                    !!!cp ('t372');
7267                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
7268                  last AFE2;                  last AFE2;
7269                }                }
7270              } # AFE2              } # AFE2
7271              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
7272                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7273                    !!!cp ('t373');
7274                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
7275                  last OE;                  last OE;
7276                }                }
7277              } # OE              } # OE
7278              last AFE;              last AFE;
7279            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
7280                !!!cp ('t374');
7281              last AFE;              last AFE;
7282            }            }
7283          } # AFE          } # AFE
7284                        
7285          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7286    
7287          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7288          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7289    
7290            !!!nack ('t374.1');
7291          !!!next-token;          !!!next-token;
7292          redo B;          next B;
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         redo B;  
7293        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7294          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7295    
7296          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7297          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7298            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7299            if ($node->[1] eq 'nobr') {            if ($node->[1] & NOBR_EL) {
7300              !!!parse-error (type => 'in nobr:nobr');              !!!cp ('t376');
7301              !!!back-token;              !!!parse-error (type => 'in nobr:nobr', token => $token);
7302              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              !!!back-token; # <nobr>
7303              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7304            } elsif ({                        line => $token->{line}, column => $token->{column}};
7305                      table => 1, caption => 1, td => 1, th => 1,              next B;
7306                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7307                     }->{$node->[1]}) {              !!!cp ('t377');
7308              last INSCOPE;              last INSCOPE;
7309            }            }
7310          } # INSCOPE          } # INSCOPE
7311                    
7312          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7313          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7314                    
7315            !!!nack ('t377.1');
7316          !!!next-token;          !!!next-token;
7317          redo B;          next B;
7318        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7319          ## has a button element in scope          ## has a button element in scope
7320          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7321            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7322            if ($node->[1] eq 'button') {            if ($node->[1] & BUTTON_EL) {
7323              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
7324              !!!back-token;              !!!parse-error (type => 'in button:button', token => $token);
7325              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              !!!back-token; # <button>
7326              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7327            } elsif ({                        line => $token->{line}, column => $token->{column}};
7328                      table => 1, caption => 1, td => 1, th => 1,              next B;
7329                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7330                     }->{$node->[1]}) {              !!!cp ('t379');
7331              last INSCOPE;              last INSCOPE;
7332            }            }
7333          } # INSCOPE          } # INSCOPE
7334                        
7335          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7336                        
7337          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7338          push @$active_formatting_elements, ['#marker', ''];  
7339            ## TODO: associate with $self->{form_element} if defined
7340    
         !!!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});  
7341          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7342            
7343          !!!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;  
             
7344          !!!next-token;          !!!next-token;
7345          redo B;          next B;
7346        } elsif ({        } elsif ({
7347                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7348                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7349                  image => 1,                  noembed => 1,
7350                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7351                    noscript => 0, ## TODO: 1 if scripting is enabled
7352                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7353          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7354            !!!parse-error (type => 'image');            !!!cp ('t381');
7355            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
7356            } else {
7357              !!!cp ('t399');
7358          }          }
7359            ## NOTE: There is an "as if in body" code clone.
7360          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7361          $reconstruct_active_formatting_elements->($insert_to_current);          next B;
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         redo B;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!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;  
7362        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7363          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7364                    
7365          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7366              !!!cp ('t389');
7367            ## Ignore the token            ## Ignore the token
7368              !!!nack ('t389'); ## NOTE: Not acknowledged.
7369            !!!next-token;            !!!next-token;
7370            redo B;            next B;
7371          } else {          } else {
7372              !!!ack ('t391.1');
7373    
7374            my $at = $token->{attributes};            my $at = $token->{attributes};
7375            my $form_attrs;            my $form_attrs;
7376            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5368  sub _tree_construction_main ($) { Line 7380  sub _tree_construction_main ($) {
7380            delete $at->{prompt};            delete $at->{prompt};
7381            my @tokens = (            my @tokens = (
7382                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7383                           attributes => $form_attrs},                           attributes => $form_attrs,
7384                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7385                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7386                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7387                            {type => START_TAG_TOKEN, tag_name => 'p',
7388                             line => $token->{line}, column => $token->{column}},
7389                            {type => START_TAG_TOKEN, tag_name => 'label',
7390                             line => $token->{line}, column => $token->{column}},
7391                         );                         );
7392            if ($prompt_attr) {            if ($prompt_attr) {
7393              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
7394                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7395                               #line => $token->{line}, column => $token->{column},
7396                              };
7397            } else {            } else {
7398                !!!cp ('t391');
7399              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7400                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7401                               #line => $token->{line}, column => $token->{column},
7402                              }; # SHOULD
7403              ## TODO: make this configurable              ## TODO: make this configurable
7404            }            }
7405            push @tokens,            push @tokens,
7406                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7407                             line => $token->{line}, column => $token->{column}},
7408                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7409                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7410                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7411                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7412                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7413            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7414                             line => $token->{line}, column => $token->{column}},
7415                            {type => END_TAG_TOKEN, tag_name => 'form',
7416                             line => $token->{line}, column => $token->{column}};
7417            !!!back-token (@tokens);            !!!back-token (@tokens);
7418            redo B;            !!!next-token;
7419              next B;
7420          }          }
7421        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7422          my $tag_name = $token->{tag_name};          ## Step 1
7423          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
7424                    
7425            ## Step 2
7426          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7427    
7428            ## Step 3
7429            $self->{ignore_newline} = 1;
7430    
7431            ## Step 4
7432            ## ISSUE: This step is wrong. (r2302 enbugged)
7433    
7434            ## Step 5
7435          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7436          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7437            
7438          $insert->($el);          ## Step 6-7
7439                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7440          my $text = '';  
7441            !!!nack ('t392.1');
7442          !!!next-token;          !!!next-token;
7443          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7444            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7445            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7446              !!!next-token;          ## has an |option| element in scope
7447            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7448              my $node = $self->{open_elements}->[$_];
7449              if ($node->[1] & OPTION_EL) {
7450                !!!cp ('t397.1');
7451                ## NOTE: As if </option>
7452                !!!back-token; # <option> or <optgroup>
7453                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7454                          line => $token->{line}, column => $token->{column}};
7455                next B;
7456              } elsif ($node->[1] & SCOPING_EL) {
7457                !!!cp ('t397.2');
7458                last INSCOPE;
7459            }            }
7460          }          } # INSCOPE
7461          while ($token->{type} == CHARACTER_TOKEN) {  
7462            $text .= $token->{data};          $reconstruct_active_formatting_elements->($insert_to_current);
7463            !!!next-token;  
7464          }          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7465          if (length $text) {  
7466            $el->manakai_append_text ($text);          !!!nack ('t397.3');
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           ## Ignore the token  
         } else {  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
7467          !!!next-token;          !!!next-token;
7468          redo B;          redo B;
7469        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7470                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7471                  noembed => 1,          ## has a |ruby| element in scope
7472                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7473                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7474                 }->{$token->{tag_name}}) {            if ($node->[1] & RUBY_EL) {
7475          ## NOTE: There is an "as if in body" code clone.              !!!cp ('t398.1');
7476          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              ## generate implied end tags
7477                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7478                  !!!cp ('t398.2');
7479                  pop @{$self->{open_elements}};
7480                }
7481                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7482                  !!!cp ('t398.3');
7483                  !!!parse-error (type => 'not closed',
7484                                  text => $self->{open_elements}->[-1]->[0]
7485                                      ->manakai_local_name,
7486                                  token => $token);
7487                  pop @{$self->{open_elements}}
7488                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7489                }
7490                last INSCOPE;
7491              } elsif ($node->[1] & SCOPING_EL) {
7492                !!!cp ('t398.4');
7493                last INSCOPE;
7494              }
7495            } # INSCOPE
7496    
7497            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7498    
7499            !!!nack ('t398.5');
7500            !!!next-token;
7501          redo B;          redo B;
7502        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7503                   $token->{tag_name} eq 'svg') {
7504          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7505    
7506            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7507    
7508            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7509    
7510            ## "adjust foreign attributes" - done in insert-element-f
7511                    
7512          !!!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);
7513                    
7514          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7515              pop @{$self->{open_elements}};
7516              !!!ack ('t398.6');
7517            } else {
7518              !!!cp ('t398.7');
7519              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7520              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7521              ## mode, "in body" (not "in foreign content") secondary insertion
7522              ## mode, maybe.
7523            }
7524    
7525          !!!next-token;          !!!next-token;
7526          redo B;          next B;
7527        } elsif ({        } elsif ({
7528                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7529                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7530                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7531                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7532                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7533          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
7534            !!!parse-error (type => 'in body',
7535                            text => $token->{tag_name}, token => $token);
7536          ## Ignore the token          ## Ignore the token
7537            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7538            !!!next-token;
7539            next B;
7540          } elsif ($token->{tag_name} eq 'param' or
7541                   $token->{tag_name} eq 'source') {
7542            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7543            pop @{$self->{open_elements}};
7544    
7545            !!!ack ('t398.5');
7546          !!!next-token;          !!!next-token;
7547          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7548        } else {        } else {
7549            if ($token->{tag_name} eq 'image') {
7550              !!!cp ('t384');
7551              !!!parse-error (type => 'image', token => $token);
7552              $token->{tag_name} = 'img';
7553            } else {
7554              !!!cp ('t385');
7555            }
7556    
7557            ## NOTE: There is an "as if <br>" code clone.
7558          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7559                    
7560          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7561    
7562            if ({
7563                 applet => 1, marquee => 1, object => 1,
7564                }->{$token->{tag_name}}) {
7565              !!!cp ('t380');
7566              push @$active_formatting_elements, ['#marker', ''];
7567              !!!nack ('t380.1');
7568            } elsif ({
7569                      b => 1, big => 1, em => 1, font => 1, i => 1,
7570                      s => 1, small => 1, strike => 1,
7571                      strong => 1, tt => 1, u => 1,
7572                     }->{$token->{tag_name}}) {
7573              !!!cp ('t375');
7574              push @$active_formatting_elements, $self->{open_elements}->[-1];
7575              !!!nack ('t375.1');
7576            } elsif ($token->{tag_name} eq 'input') {
7577              !!!cp ('t388');
7578              ## TODO: associate with $self->{form_element} if defined
7579              pop @{$self->{open_elements}};
7580              !!!ack ('t388.2');
7581            } elsif ({
7582                      area => 1, basefont => 1, bgsound => 1, br => 1,
7583                      embed => 1, img => 1, spacer => 1, wbr => 1,
7584                     }->{$token->{tag_name}}) {
7585              !!!cp ('t388.1');
7586              pop @{$self->{open_elements}};
7587              !!!ack ('t388.3');
7588            } elsif ($token->{tag_name} eq 'select') {
7589              ## TODO: associate with $self->{form_element} if defined
7590            
7591              if ($self->{insertion_mode} & TABLE_IMS or
7592                  $self->{insertion_mode} & BODY_TABLE_IMS or
7593                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7594                !!!cp ('t400.1');
7595                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7596              } else {
7597                !!!cp ('t400.2');
7598                $self->{insertion_mode} = IN_SELECT_IM;
7599              }
7600              !!!nack ('t400.3');
7601            } else {
7602              !!!nack ('t402');
7603            }
7604                    
7605          !!!next-token;          !!!next-token;
7606          redo B;          next B;
7607        }        }
7608      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7609        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7610          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7611              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7612            for (@{$self->{open_elements}}) {          INSCOPE: {
7613              unless ({            for (reverse @{$self->{open_elements}}) {
7614                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] & BODY_EL) {
7615                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7616                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7617                      }->{$_->[1]}) {                last INSCOPE;
7618                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
7619                  !!!cp ('t405.1');
7620                  last;
7621              }              }
7622            }            }
7623    
7624            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7625            !!!next-token;  
7626            redo B;            !!!parse-error (type => 'unmatched end tag',
7627          } else {                            text => $token->{tag_name}, token => $token);
7628            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: Ignore the token.
           ## Ignore the token  
7629            !!!next-token;            !!!next-token;
7630            redo B;            next B;
7631            } # INSCOPE
7632    
7633            for (@{$self->{open_elements}}) {
7634              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7635                !!!cp ('t403');
7636                !!!parse-error (type => 'not closed',
7637                                text => $_->[0]->manakai_local_name,
7638                                token => $token);
7639                last;
7640              } else {
7641                !!!cp ('t404');
7642              }
7643          }          }
7644    
7645            $self->{insertion_mode} = AFTER_BODY_IM;
7646            !!!next-token;
7647            next B;
7648        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7649          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## TODO: Update this code.  It seems that the code below is not
7650            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7651            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7652              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              $self->{open_elements}->[1]->[1] & BODY_EL) {
7653              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7654                !!!cp ('t406');
7655                !!!parse-error (type => 'not closed',
7656                                text => $self->{open_elements}->[1]->[0]
7657                                    ->manakai_local_name,
7658                                token => $token);
7659              } else {
7660                !!!cp ('t407');
7661            }            }
7662            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7663            ## reprocess            ## reprocess
7664            redo B;            next B;
7665          } else {          } else {
7666            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
7667              !!!parse-error (type => 'unmatched end tag',
7668                              text => $token->{tag_name}, token => $token);
7669            ## Ignore the token            ## Ignore the token
7670            !!!next-token;            !!!next-token;
7671            redo B;            next B;
7672          }          }
7673        } elsif ({        } elsif ({
7674                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7675                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7676                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7677                  p => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
7678                    center => 1, datagrid => 1, details => 1, dialog => 1,
7679                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7680                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7681                    ol => 1, pre => 1, section => 1, ul => 1,
7682    
7683                    ## NOTE: As normal, but ... optional tags
7684                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7685                  button => 1, marquee => 1, object => 1,  
7686                    applet => 1, button => 1, marquee => 1, object => 1,
7687                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7688            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7689            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7690            ## </dd>" code.
7691    
7692          ## has an element in scope          ## has an element in scope
7693          my $i;          my $i;
7694          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7695            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7696            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7697              ## 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;  
             }  
7698              $i = $_;              $i = $_;
7699              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7700            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7701                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7702              last INSCOPE;              last INSCOPE;
7703            }            }
7704          } # INSCOPE          } # INSCOPE
7705            
7706          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7707            if (defined $i) {            !!!cp ('t413');
7708              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag',
7709                              text => $token->{tag_name}, token => $token);
7710              ## NOTE: Ignore the token.
7711            } else {
7712              ## Step 1. generate implied end tags
7713              while ({
7714                      ## END_TAG_OPTIONAL_EL
7715                      dd => ($token->{tag_name} ne 'dd'),
7716                      dt => ($token->{tag_name} ne 'dt'),
7717                      li => ($token->{tag_name} ne 'li'),
7718                      option => 1,
7719                      optgroup => 1,
7720                      p => 1,
7721                      rt => 1,
7722                      rp => 1,
7723                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7724                !!!cp ('t409');
7725                pop @{$self->{open_elements}};
7726              }
7727    
7728              ## Step 2.
7729              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7730                      ne $token->{tag_name}) {
7731                !!!cp ('t412');
7732                !!!parse-error (type => 'not closed',
7733                                text => $self->{open_elements}->[-1]->[0]
7734                                    ->manakai_local_name,
7735                                token => $token);
7736            } else {            } else {
7737              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
7738            }            }
7739          }  
7740                      ## Step 3.
         if (defined $i) {  
7741            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7742          } elsif ($token->{tag_name} eq 'p') {  
7743            ## As if <p>, then reprocess the current token            ## Step 4.
7744            my $el;            $clear_up_to_marker->()
7745            !!!create-element ($el, 'p');                if {
7746            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
7747                  }->{$token->{tag_name}};
7748          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
7749          !!!next-token;          !!!next-token;
7750          redo B;          next B;
7751        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7752            ## NOTE: As normal, but interacts with the form element pointer
7753    
7754            undef $self->{form_element};
7755    
7756          ## has an element in scope          ## has an element in scope
7757            my $i;
7758          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7759            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7760            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] & FORM_EL) {
7761              ## generate implied end tags              !!!cp ('t418');
7762              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;  
             }  
7763              last INSCOPE;              last INSCOPE;
7764            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7765                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7766              last INSCOPE;              last INSCOPE;
7767            }            }
7768          } # INSCOPE          } # INSCOPE
7769            
7770          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7771            pop @{$self->{open_elements}};            !!!cp ('t421');
7772          } else {            !!!parse-error (type => 'unmatched end tag',
7773            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
7774              ## NOTE: Ignore the token.
7775            } else {
7776              ## Step 1. generate implied end tags
7777              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7778                !!!cp ('t417');
7779                pop @{$self->{open_elements}};
7780              }
7781              
7782              ## Step 2.
7783              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7784                      ne $token->{tag_name}) {
7785                !!!cp ('t417.1');
7786                !!!parse-error (type => 'not closed',
7787                                text => $self->{open_elements}->[-1]->[0]
7788                                    ->manakai_local_name,
7789                                token => $token);
7790              } else {
7791                !!!cp ('t420');
7792              }  
7793              
7794              ## Step 3.
7795              splice @{$self->{open_elements}}, $i;
7796          }          }
7797    
         undef $self->{form_element};  
7798          !!!next-token;          !!!next-token;
7799          redo B;          next B;
7800        } elsif ({        } elsif ({
7801                    ## NOTE: As normal, except acts as a closer for any ...
7802                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7803                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7804          ## has an element in scope          ## has an element in scope
7805          my $i;          my $i;
7806          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7807            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7808            if ({            if ($node->[1] & HEADING_EL) {
7809                 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;  
             }  
7810              $i = $_;              $i = $_;
7811              last INSCOPE;              last INSCOPE;
7812            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7813                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7814              last INSCOPE;              last INSCOPE;
7815            }            }
7816          } # INSCOPE          } # INSCOPE
7817            
7818          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7819            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t425.1');
7820              !!!parse-error (type => 'unmatched end tag',
7821                              text => $token->{tag_name}, token => $token);
7822              ## NOTE: Ignore the token.
7823            } else {
7824              ## Step 1. generate implied end tags
7825              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7826                !!!cp ('t422');
7827                pop @{$self->{open_elements}};
7828              }
7829              
7830              ## Step 2.
7831              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7832                      ne $token->{tag_name}) {
7833                !!!cp ('t425');
7834                !!!parse-error (type => 'unmatched end tag',
7835                                text => $token->{tag_name}, token => $token);
7836              } else {
7837                !!!cp ('t426');
7838              }
7839    
7840              ## Step 3.
7841              splice @{$self->{open_elements}}, $i;
7842          }          }
7843                    
         splice @{$self->{open_elements}}, $i if defined $i;  
7844          !!!next-token;          !!!next-token;
7845          redo B;          next B;
7846          } elsif ($token->{tag_name} eq 'p') {
7847            ## NOTE: As normal, except </p> implies <p> and ...
7848    
7849            ## has an element in scope
7850            my $non_optional;
7851            my $i;
7852            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7853              my $node = $self->{open_elements}->[$_];
7854              if ($node->[1] & P_EL) {
7855                !!!cp ('t410.1');
7856                $i = $_;
7857                last INSCOPE;
7858              } elsif ($node->[1] & SCOPING_EL) {
7859                !!!cp ('t411.1');
7860                last INSCOPE;
7861              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7862                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7863                !!!cp ('t411.2');
7864                #
7865              } else {
7866                !!!cp ('t411.3');
7867                $non_optional ||= $node;
7868                #
7869              }
7870            } # INSCOPE
7871    
7872            if (defined $i) {
7873              ## 1. Generate implied end tags
7874              #
7875    
7876              ## 2. If current node != "p", parse error
7877              if ($non_optional) {
7878                !!!cp ('t412.1');
7879                !!!parse-error (type => 'not closed',
7880                                text => $non_optional->[0]->manakai_local_name,
7881                                token => $token);
7882              } else {
7883                !!!cp ('t414.1');
7884              }
7885    
7886              ## 3. Pop
7887              splice @{$self->{open_elements}}, $i;
7888            } else {
7889              !!!cp ('t413.1');
7890              !!!parse-error (type => 'unmatched end tag',
7891                              text => $token->{tag_name}, token => $token);
7892    
7893              !!!cp ('t415.1');
7894              ## As if <p>, then reprocess the current token
7895              my $el;
7896              !!!create-element ($el, $HTML_NS, 'p',, $token);
7897              $insert->($el);
7898              ## NOTE: Not inserted into |$self->{open_elements}|.
7899            }
7900    
7901            !!!next-token;
7902            next B;
7903        } elsif ({        } elsif ({
7904                  a => 1,                  a => 1,
7905                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7906                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7907                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7908                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7909          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
7910          redo B;          $formatting_end_tag->($token);
7911            next B;
7912        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7913          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
7914            !!!parse-error (type => 'unmatched end tag',
7915                            text => 'br', token => $token);
7916    
7917          ## As if <br>          ## As if <br>
7918          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7919                    
7920          my $el;          my $el;
7921          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7922          $insert->($el);          $insert->($el);
7923                    
7924          ## Ignore the token.          ## Ignore the token.
7925          !!!next-token;          !!!next-token;
7926          redo B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
7927        } else {        } else {
7928            if ($token->{tag_name} eq 'sarcasm') {
7929              sleep 0.001; # take a deep breath
7930            }
7931    
7932          ## Step 1          ## Step 1
7933          my $node_i = -1;          my $node_i = -1;
7934          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7935    
7936          ## Step 2          ## Step 2
7937          S2: {          S2: {
7938            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7939              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7940              if ($node_tag_name eq $token->{tag_name}) {
7941              ## Step 1              ## Step 1
7942              ## generate implied end tags              ## generate implied end tags
7943              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7944                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
7945                   td => 1, th => 1, tr => 1,                ## NOTE: |<ruby><rt></ruby>|.
7946                   tbody => 1, tfoot => 1, thead => 1,                ## ISSUE: <ruby><rt></rt> will also take this code path,
7947                  }->{$self->{open_elements}->[-1]->[1]}) {                ## which seems wrong.
7948                !!!back-token;                pop @{$self->{open_elements}};
7949                $token = {type => END_TAG_TOKEN,                $node_i++;
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
7950              }              }
7951                    
7952              ## Step 2              ## Step 2
7953              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7954                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7955                $current_tag_name =~ tr/A-Z/a-z/;
7956                if ($current_tag_name ne $token->{tag_name}) {
7957                  !!!cp ('t431');
7958                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7959                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7960                                  text => $self->{open_elements}->[-1]->[0]
7961                                      ->manakai_local_name,
7962                                  token => $token);
7963                } else {
7964                  !!!cp ('t432');
7965              }              }
7966                            
7967              ## Step 3              ## Step 3
7968              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7969    
7970              !!!next-token;              !!!next-token;
7971              last S2;              last S2;
7972            } else {            } else {
7973              ## Step 3              ## Step 3
7974              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7975                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7976                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7977                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7978                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
7979                  !!!parse-error (type => 'unmatched end tag',
7980                                  text => $token->{tag_name}, token => $token);
7981                ## Ignore the token                ## Ignore the token
7982                !!!next-token;                !!!next-token;
7983                last S2;                last S2;
7984    
7985                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7986                  ## 9.27, "a" is a child of <dd> (conforming).  In
7987                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7988                  ## "a" is a child of both <body> and <dd>.
7989              }              }
7990                
7991                !!!cp ('t434');
7992            }            }
7993                        
7994            ## Step 4            ## Step 4
# Line 5726  sub _tree_construction_main ($) { Line 7998  sub _tree_construction_main ($) {
7998            ## Step 5;            ## Step 5;
7999            redo S2;            redo S2;
8000          } # S2          } # S2
8001          redo B;          next B;
8002        }        }
8003      }      }
8004      redo B;      next B;
8005      } continue { # B
8006        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8007          ## NOTE: The code below is executed in cases where it does not have
8008          ## to be, but it it is harmless even in those cases.
8009          ## has an element in scope
8010          INSCOPE: {
8011            for (reverse 0..$#{$self->{open_elements}}) {
8012              my $node = $self->{open_elements}->[$_];
8013              if ($node->[1] & FOREIGN_EL) {
8014                last INSCOPE;
8015              } elsif ($node->[1] & SCOPING_EL) {
8016                last;
8017              }
8018            }
8019            
8020            ## NOTE: No foreign element in scope.
8021            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8022          } # INSCOPE
8023        }
8024    } # B    } # B
8025    
   ## 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  
   
8026    ## Stop parsing # MUST    ## Stop parsing # MUST
8027        
8028    ## TODO: script stuffs    ## TODO: script stuffs
8029  } # _tree_construct_main  } # _tree_construct_main
8030    
8031  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8032    my $class = shift;    my $class = shift;
8033    my $node = shift;    my $node = shift;
8034    my $s = \$_[0];    #my $s = \$_[0];
8035    my $onerror = $_[1];    my $onerror = $_[1];
8036      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8037    
8038    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8039    
# Line 5765  sub set_inner_html ($$$) { Line 8052  sub set_inner_html ($$$) {
8052      }      }
8053    
8054      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8055      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8056    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8057      ## TODO: If non-html element      ## TODO: If non-html element
8058    
8059      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8060    
8061    ## TODO: Support for $get_wrapper
8062    
8063      ## Step 1 # MUST      ## Step 1 # MUST
8064      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8065      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5778  sub set_inner_html ($$$) { Line 8067  sub set_inner_html ($$$) {
8067      my $p = $class->new;      my $p = $class->new;
8068      $p->{document} = $doc;      $p->{document} = $doc;
8069    
8070      ## Step 9 # MUST      ## Step 8 # MUST
8071      my $i = 0;      my $i = 0;
8072      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8073      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8074      $p->{set_next_char} = sub {      require Whatpm::Charset::DecodeHandle;
8075        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8076        $input = $get_wrapper->($input);
8077        $p->{set_nc} = sub {
8078        my $self = shift;        my $self = shift;
8079    
8080        pop @{$self->{prev_char}};        my $char = '';
8081        unshift @{$self->{prev_char}}, $self->{next_char};        if (defined $self->{next_nc}) {
8082            $char = $self->{next_nc};
8083            delete $self->{next_nc};
8084            $self->{nc} = ord $char;
8085          } else {
8086            $self->{char_buffer} = '';
8087            $self->{char_buffer_pos} = 0;
8088            
8089            my $count = $input->manakai_read_until
8090                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8091                 $self->{char_buffer_pos});
8092            if ($count) {
8093              $self->{line_prev} = $self->{line};
8094              $self->{column_prev} = $self->{column};
8095              $self->{column}++;
8096              $self->{nc}
8097                  = ord substr ($self->{char_buffer},
8098                                $self->{char_buffer_pos}++, 1);
8099              return;
8100            }
8101            
8102            if ($input->read ($char, 1)) {
8103              $self->{nc} = ord $char;
8104            } else {
8105              $self->{nc} = -1;
8106              return;
8107            }
8108          }
8109    
8110        $self->{next_char} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8111        $self->{next_char} = ord substr $$s, $i++, 1;        $p->{column}++;
8112        $column++;  
8113          if ($self->{nc} == 0x000A) { # LF
8114        if ($self->{next_char} == 0x000A) { # LF          $p->{line}++;
8115          $line++;          $p->{column} = 0;
8116          $column = 0;          !!!cp ('i1');
8117        } elsif ($self->{next_char} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8118          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8119          $self->{next_char} = 0x000A; # LF # MUST          my $next = '';
8120          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8121          $column = 0;            $self->{next_nc} = $next;
8122        } elsif ($self->{next_char} > 0x10FFFF) {          }
8123          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
8124        } elsif ($self->{next_char} == 0x0000) { # NULL          $p->{line}++;
8125            $p->{column} = 0;
8126            !!!cp ('i2');
8127          } elsif ($self->{nc} == 0x0000) { # NULL
8128            !!!cp ('i4');
8129          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8130          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8131        }        }
8132      };      };
8133      $p->{prev_char} = [-1, -1, -1];  
8134      $p->{next_char} = -1;      $p->{read_until} = sub {
8135              #my ($scalar, $specials_range, $offset) = @_;
8136          return 0 if defined $p->{next_nc};
8137    
8138          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8139          my $offset = $_[2] || 0;
8140          
8141          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8142            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8143            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8144              substr ($_[0], $offset)
8145                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8146              my $count = $+[0] - $-[0];
8147              if ($count) {
8148                $p->{column} += $count;
8149                $p->{char_buffer_pos} += $count;
8150                $p->{line_prev} = $p->{line};
8151                $p->{column_prev} = $p->{column} - 1;
8152                $p->{nc} = -1;
8153              }
8154              return $count;
8155            } else {
8156              return 0;
8157            }
8158          } else {
8159            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8160            if ($count) {
8161              $p->{column} += $count;
8162              $p->{column_prev} += $count;
8163              $p->{nc} = -1;
8164            }
8165            return $count;
8166          }
8167        }; # $p->{read_until}
8168    
8169      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8170        my (%opt) = @_;        my (%opt) = @_;
8171        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8172          my $column = $opt{column};
8173          if (defined $opt{token} and defined $opt{token}->{line}) {
8174            $line = $opt{token}->{line};
8175            $column = $opt{token}->{column};
8176          }
8177          warn "Parse error ($opt{type}) at line $line column $column\n";
8178      };      };
8179      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8180        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8181      };      };
8182            
8183        my $char_onerror = sub {
8184          my (undef, $type, %opt) = @_;
8185          $ponerror->(layer => 'encode',
8186                      line => $p->{line}, column => $p->{column} + 1,
8187                      %opt, type => $type);
8188        }; # $char_onerror
8189        $input->onerror ($char_onerror);
8190    
8191      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8192      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8193    
# Line 5839  sub set_inner_html ($$$) { Line 8209  sub set_inner_html ($$$) {
8209          unless defined $p->{content_model};          unless defined $p->{content_model};
8210          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8211    
8212      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8213          ## TODO: Foreign element OK?
8214    
8215      ## Step 4      ## Step 3
8216      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8217        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8218    
8219      ## Step 5 # MUST      ## Step 4 # MUST
8220      $doc->append_child ($root);      $doc->append_child ($root);
8221    
8222      ## Step 6 # MUST      ## Step 5 # MUST
8223      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8224    
8225      undef $p->{head_element};      undef $p->{head_element};
8226        undef $p->{head_element_inserted};
8227    
8228      ## Step 7 # MUST      ## Step 6 # MUST
8229      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8230    
8231      ## Step 8 # MUST      ## Step 7 # MUST
8232      my $anode = $node;      my $anode = $node;
8233      AN: while (defined $anode) {      AN: while (defined $anode) {
8234        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8235          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8236          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8237            if ($anode->manakai_local_name eq 'form') {            if ($anode->manakai_local_name eq 'form') {
8238                !!!cp ('i5');
8239              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8240              last AN;              last AN;
8241            }            }
# Line 5871  sub set_inner_html ($$$) { Line 8244  sub set_inner_html ($$$) {
8244        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8245      } # AN      } # AN
8246            
8247      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8248      {      {
8249        my $self = $p;        my $self = $p;
8250        !!!next-token;        !!!next-token;
8251      }      }
8252      $p->_tree_construction_main;      $p->_tree_construction_main;
8253    
8254      ## Step 11 # MUST      ## Step 10 # MUST
8255      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8256      for (@cn) {      for (@cn) {
8257        $node->remove_child ($_);        $node->remove_child ($_);
8258      }      }
8259      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8260    
8261      ## Step 12 # MUST      ## Step 11 # MUST
8262      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8263      for (@cn) {      for (@cn) {
8264        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5895  sub set_inner_html ($$$) { Line 8267  sub set_inner_html ($$$) {
8267      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8268    
8269      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8270    
8271        delete $p->{parse_error}; # delete loop
8272    } else {    } else {
8273      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8274    }    }

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24