/[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.72 by wakaba, Sun Mar 2 14:32:26 2008 UTC revision 1.206 by wakaba, Mon Oct 13 08:22:30 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,  ## Bits 12-15
32    img => 1,  sub SPECIAL_EL () { 0b1_000000000000000 }
33    embed => 1,  sub SCOPING_EL () { 0b1_00000000000000 }
34    param => 1,  sub FORMATTING_EL () { 0b1_0000000000000 }
35    area => 1,  sub PHRASING_EL () { 0b1_000000000000 }
36    col => 1,  
37    input => 1,  ## Bits 10-11
38    sub FOREIGN_EL () { 0b1_00000000000 }
39    sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
40    
41    ## Bits 6-9
42    sub TABLE_SCOPING_EL () { 0b1_000000000 }
43    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
44    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
45    sub TABLE_ROWS_EL () { 0b1_000000 }
46    
47    ## Bit 5
48    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
49    
50    ## NOTE: Used in </body> and EOF algorithms.
51    ## Bit 4
52    sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
53    
54    ## NOTE: Used in "generate implied end tags" algorithm.
55    ## NOTE: There is a code where a modified version of
56    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
57    ## implementation (search for the algorithm name).
58    ## Bit 3
59    sub END_TAG_OPTIONAL_EL () { 0b1_000 }
60    
61    ## Bits 0-2
62    
63    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
64    sub FORM_EL () { SPECIAL_EL | 0b001 }
65    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
66    sub HEADING_EL () { SPECIAL_EL | 0b011 }
67    sub SELECT_EL () { SPECIAL_EL | 0b100 }
68    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
69    
70    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
71    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
72    
73    sub DD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b001
78    }
79    sub DT_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84    }
85    sub LI_EL () {
86      SPECIAL_EL |
87      END_TAG_OPTIONAL_EL |
88      ALL_END_TAG_OPTIONAL_EL |
89      0b100
90    }
91    sub P_EL () {
92      SPECIAL_EL |
93      ADDRESS_DIV_P_EL |
94      END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97    }
98    
99    sub TABLE_ROW_EL () {
100      SPECIAL_EL |
101      TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112    }
113    
114    sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    sub HTML_EL () {
118      SCOPING_EL |
119      TABLE_SCOPING_EL |
120      TABLE_ROWS_SCOPING_EL |
121      TABLE_ROW_SCOPING_EL |
122      ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151    my $el_category = {
152      a => A_EL,
153      address => ADDRESS_DIV_EL,
154      applet => MISC_SCOPING_EL,
155      area => MISC_SPECIAL_EL,
156      article => MISC_SPECIAL_EL,
157      aside => MISC_SPECIAL_EL,
158      b => FORMATTING_EL,
159      base => MISC_SPECIAL_EL,
160      basefont => MISC_SPECIAL_EL,
161      bgsound => MISC_SPECIAL_EL,
162      big => FORMATTING_EL,
163      blockquote => MISC_SPECIAL_EL,
164      body => BODY_EL,
165      br => MISC_SPECIAL_EL,
166      button => BUTTON_EL,
167      caption => CAPTION_EL,
168      center => MISC_SPECIAL_EL,
169      col => MISC_SPECIAL_EL,
170      colgroup => MISC_SPECIAL_EL,
171      command => MISC_SPECIAL_EL,
172      datagrid => MISC_SPECIAL_EL,
173      dd => DD_EL,
174      details => MISC_SPECIAL_EL,
175      dialog => MISC_SPECIAL_EL,
176      dir => MISC_SPECIAL_EL,
177      div => ADDRESS_DIV_EL,
178      dl => MISC_SPECIAL_EL,
179      dt => DT_EL,
180      em => FORMATTING_EL,
181      embed => MISC_SPECIAL_EL,
182      eventsource => MISC_SPECIAL_EL,
183      fieldset => MISC_SPECIAL_EL,
184      figure => MISC_SPECIAL_EL,
185      font => FORMATTING_EL,
186      footer => MISC_SPECIAL_EL,
187      form => FORM_EL,
188      frame => MISC_SPECIAL_EL,
189      frameset => FRAMESET_EL,
190      h1 => HEADING_EL,
191      h2 => HEADING_EL,
192      h3 => HEADING_EL,
193      h4 => HEADING_EL,
194      h5 => HEADING_EL,
195      h6 => HEADING_EL,
196      head => MISC_SPECIAL_EL,
197      header => MISC_SPECIAL_EL,
198      hr => MISC_SPECIAL_EL,
199      html => HTML_EL,
200      i => FORMATTING_EL,
201      iframe => MISC_SPECIAL_EL,
202      img => MISC_SPECIAL_EL,
203      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
204      input => MISC_SPECIAL_EL,
205      isindex => MISC_SPECIAL_EL,
206      li => LI_EL,
207      link => MISC_SPECIAL_EL,
208      listing => MISC_SPECIAL_EL,
209      marquee => MISC_SCOPING_EL,
210      menu => MISC_SPECIAL_EL,
211      meta => MISC_SPECIAL_EL,
212      nav => MISC_SPECIAL_EL,
213      nobr => NOBR_EL,
214      noembed => MISC_SPECIAL_EL,
215      noframes => MISC_SPECIAL_EL,
216      noscript => MISC_SPECIAL_EL,
217      object => MISC_SCOPING_EL,
218      ol => MISC_SPECIAL_EL,
219      optgroup => OPTGROUP_EL,
220      option => OPTION_EL,
221      p => P_EL,
222      param => MISC_SPECIAL_EL,
223      plaintext => MISC_SPECIAL_EL,
224      pre => MISC_SPECIAL_EL,
225      rp => RUBY_COMPONENT_EL,
226      rt => RUBY_COMPONENT_EL,
227      ruby => RUBY_EL,
228      s => FORMATTING_EL,
229      script => MISC_SPECIAL_EL,
230      select => SELECT_EL,
231      section => MISC_SPECIAL_EL,
232      small => FORMATTING_EL,
233      spacer => MISC_SPECIAL_EL,
234      strike => FORMATTING_EL,
235      strong => FORMATTING_EL,
236      style => MISC_SPECIAL_EL,
237      table => TABLE_EL,
238      tbody => TABLE_ROW_GROUP_EL,
239      td => TABLE_CELL_EL,
240      textarea => MISC_SPECIAL_EL,
241      tfoot => TABLE_ROW_GROUP_EL,
242      th => TABLE_CELL_EL,
243      thead => TABLE_ROW_GROUP_EL,
244      title => MISC_SPECIAL_EL,
245      tr => TABLE_ROW_EL,
246      tt => FORMATTING_EL,
247      u => FORMATTING_EL,
248      ul => MISC_SPECIAL_EL,
249      wbr => MISC_SPECIAL_EL,
250    };
251    
252    my $el_category_f = {
253      $MML_NS => {
254        'annotation-xml' => MML_AXML_EL,
255        mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256        mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257        mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258        ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259        mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      },
261      $SVG_NS => {
262        foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263        desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264        title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      },
266      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267    };
268    
269    my $svg_attr_name = {
270      attributename => 'attributeName',
271      attributetype => 'attributeType',
272      basefrequency => 'baseFrequency',
273      baseprofile => 'baseProfile',
274      calcmode => 'calcMode',
275      clippathunits => 'clipPathUnits',
276      contentscripttype => 'contentScriptType',
277      contentstyletype => 'contentStyleType',
278      diffuseconstant => 'diffuseConstant',
279      edgemode => 'edgeMode',
280      externalresourcesrequired => 'externalResourcesRequired',
281      filterres => 'filterRes',
282      filterunits => 'filterUnits',
283      glyphref => 'glyphRef',
284      gradienttransform => 'gradientTransform',
285      gradientunits => 'gradientUnits',
286      kernelmatrix => 'kernelMatrix',
287      kernelunitlength => 'kernelUnitLength',
288      keypoints => 'keyPoints',
289      keysplines => 'keySplines',
290      keytimes => 'keyTimes',
291      lengthadjust => 'lengthAdjust',
292      limitingconeangle => 'limitingConeAngle',
293      markerheight => 'markerHeight',
294      markerunits => 'markerUnits',
295      markerwidth => 'markerWidth',
296      maskcontentunits => 'maskContentUnits',
297      maskunits => 'maskUnits',
298      numoctaves => 'numOctaves',
299      pathlength => 'pathLength',
300      patterncontentunits => 'patternContentUnits',
301      patterntransform => 'patternTransform',
302      patternunits => 'patternUnits',
303      pointsatx => 'pointsAtX',
304      pointsaty => 'pointsAtY',
305      pointsatz => 'pointsAtZ',
306      preservealpha => 'preserveAlpha',
307      preserveaspectratio => 'preserveAspectRatio',
308      primitiveunits => 'primitiveUnits',
309      refx => 'refX',
310      refy => 'refY',
311      repeatcount => 'repeatCount',
312      repeatdur => 'repeatDur',
313      requiredextensions => 'requiredExtensions',
314      requiredfeatures => 'requiredFeatures',
315      specularconstant => 'specularConstant',
316      specularexponent => 'specularExponent',
317      spreadmethod => 'spreadMethod',
318      startoffset => 'startOffset',
319      stddeviation => 'stdDeviation',
320      stitchtiles => 'stitchTiles',
321      surfacescale => 'surfaceScale',
322      systemlanguage => 'systemLanguage',
323      tablevalues => 'tableValues',
324      targetx => 'targetX',
325      targety => 'targetY',
326      textlength => 'textLength',
327      viewbox => 'viewBox',
328      viewtarget => 'viewTarget',
329      xchannelselector => 'xChannelSelector',
330      ychannelselector => 'yChannelSelector',
331      zoomandpan => 'zoomAndPan',
332    };
333    
334    my $foreign_attr_xname = {
335      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
336      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
337      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
338      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
339      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
340      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
341      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
342      'xml:base' => [$XML_NS, ['xml', 'base']],
343      'xml:lang' => [$XML_NS, ['xml', 'lang']],
344      'xml:space' => [$XML_NS, ['xml', 'space']],
345      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
346      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
347  };  };
348    
349  my $c1_entity_char = {  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
351    my $charref_map = {
352      0x0D => 0x000A,
353    0x80 => 0x20AC,    0x80 => 0x20AC,
354    0x81 => 0xFFFD,    0x81 => 0xFFFD,
355    0x82 => 0x201A,    0x82 => 0x201A,
# Line 59  my $c1_entity_char = { Line 382  my $c1_entity_char = {
382    0x9D => 0xFFFD,    0x9D => 0xFFFD,
383    0x9E => 0x017E,    0x9E => 0x017E,
384    0x9F => 0x0178,    0x9F => 0x0178,
385  }; # $c1_entity_char  }; # $charref_map
386    $charref_map->{$_} = 0xFFFD
387        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
388            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
389            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
390            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
391            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
392            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
393            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
394    
395  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
396    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  
397    
398  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
399      my $self = shift;
400      my $charset_name = shift;
401      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
402      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
403    } # parse_byte_string
404    
405    sub parse_byte_stream ($$$$;$$) {
406      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
407    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
408    my $charset = shift;    my $charset_name = shift;
409    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;  
   }  
410    
411    $self->{change_encoding} = sub {    my $onerror = $_[2] || sub {
412      my $self = shift;      my (%opt) = @_;
413      my $charset = lc shift;      warn "Parse error ($opt{type})\n";
414      ## TODO: if $charset is supported    };
415      ## TODO: normalize charset name    $self->{parse_error} = $onerror; # updated later by parse_char_string
416    
417      ## "Change the encoding" algorithm:    my $get_wrapper = $_[3] || sub ($) {
418        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
419      ## Step 1        };
420      if ($charset eq 'utf-16') { ## ISSUE: UTF-16BE -> UTF-8? UTF-16LE -> UTF-8?  
421        $charset = 'utf-8';    ## HTML5 encoding sniffing algorithm
422      require Message::Charset::Info;
423      my $charset;
424      my $buffer;
425      my ($char_stream, $e_status);
426    
427      SNIFFING: {
428        ## NOTE: By setting |allow_fallback| option true when the
429        ## |get_decode_handle| method is invoked, we ignore what the HTML5
430        ## spec requires, i.e. unsupported encoding should be ignored.
431          ## TODO: We should not do this unless the parser is invoked
432          ## in the conformance checking mode, in which this behavior
433          ## would be useful.
434    
435        ## Step 1
436        if (defined $charset_name) {
437          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
438              ## TODO: Is this ok?  Transfer protocol's parameter should be
439              ## interpreted in its semantics?
440    
441          ($char_stream, $e_status) = $charset->get_decode_handle
442              ($byte_stream, allow_error_reporting => 1,
443               allow_fallback => 1);
444          if ($char_stream) {
445            $self->{confident} = 1;
446            last SNIFFING;
447          } else {
448            !!!parse-error (type => 'charset:not supported',
449                            layer => 'encode',
450                            line => 1, column => 1,
451                            value => $charset_name,
452                            level => $self->{level}->{uncertain});
453          }
454      }      }
455    
456      ## Step 2      ## Step 2
457      if (defined $self->{input_encoding} and      my $byte_buffer = '';
458          $self->{input_encoding} eq $charset) {      for (1..1024) {
459          my $char = $byte_stream->getc;
460          last unless defined $char;
461          $byte_buffer .= $char;
462        } ## TODO: timeout
463    
464        ## Step 3
465        if ($byte_buffer =~ /^\xFE\xFF/) {
466          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
467          ($char_stream, $e_status) = $charset->get_decode_handle
468              ($byte_stream, allow_error_reporting => 1,
469               allow_fallback => 1, byte_buffer => \$byte_buffer);
470        $self->{confident} = 1;        $self->{confident} = 1;
471        return;        last SNIFFING;
472        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
473          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
474          ($char_stream, $e_status) = $charset->get_decode_handle
475              ($byte_stream, allow_error_reporting => 1,
476               allow_fallback => 1, byte_buffer => \$byte_buffer);
477          $self->{confident} = 1;
478          last SNIFFING;
479        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
480          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
481          ($char_stream, $e_status) = $charset->get_decode_handle
482              ($byte_stream, allow_error_reporting => 1,
483               allow_fallback => 1, byte_buffer => \$byte_buffer);
484          $self->{confident} = 1;
485          last SNIFFING;
486      }      }
487    
488      !!!parse-error (type => 'charset label detected:'.$self->{input_encoding}.      ## Step 4
489          ':'.$charset, level => 'w');      ## TODO: <meta charset>
490    
491      ## Step 3      ## Step 5
492      # if (can) {      ## TODO: from history
       ## change the encoding on the fly.  
       #$self->{confident} = 1;  
       #return;  
     # }  
493    
494      ## Step 4      ## Step 6
495      throw Whatpm::HTML::RestartParser (charset => $charset);      require Whatpm::Charset::UniversalCharDet;
496        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
497            ($byte_buffer);
498        if (defined $charset_name) {
499          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
500    
501          require Whatpm::Charset::DecodeHandle;
502          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
503              ($byte_stream);
504          ($char_stream, $e_status) = $charset->get_decode_handle
505              ($buffer, allow_error_reporting => 1,
506               allow_fallback => 1, byte_buffer => \$byte_buffer);
507          if ($char_stream) {
508            $buffer->{buffer} = $byte_buffer;
509            !!!parse-error (type => 'sniffing:chardet',
510                            text => $charset_name,
511                            level => $self->{level}->{info},
512                            layer => 'encode',
513                            line => 1, column => 1);
514            $self->{confident} = 0;
515            last SNIFFING;
516          }
517        }
518    
519        ## Step 7: default
520        ## TODO: Make this configurable.
521        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
522            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
523            ## detectable in the step 6.
524        require Whatpm::Charset::DecodeHandle;
525        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
526            ($byte_stream);
527        ($char_stream, $e_status)
528            = $charset->get_decode_handle ($buffer,
529                                           allow_error_reporting => 1,
530                                           allow_fallback => 1,
531                                           byte_buffer => \$byte_buffer);
532        $buffer->{buffer} = $byte_buffer;
533        !!!parse-error (type => 'sniffing:default',
534                        text => 'windows-1252',
535                        level => $self->{level}->{info},
536                        line => 1, column => 1,
537                        layer => 'encode');
538        $self->{confident} = 0;
539      } # SNIFFING
540    
541      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
542        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
543        !!!parse-error (type => 'chardecode:fallback',
544                        #text => $self->{input_encoding},
545                        level => $self->{level}->{uncertain},
546                        line => 1, column => 1,
547                        layer => 'encode');
548      } elsif (not ($e_status &
549                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
550        $self->{input_encoding} = $charset->get_iana_name;
551        !!!parse-error (type => 'chardecode:no error',
552                        text => $self->{input_encoding},
553                        level => $self->{level}->{uncertain},
554                        line => 1, column => 1,
555                        layer => 'encode');
556      } else {
557        $self->{input_encoding} = $charset->get_iana_name;
558      }
559    
560      $self->{change_encoding} = sub {
561        my $self = shift;
562        $charset_name = shift;
563        my $token = shift;
564    
565        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
566        ($char_stream, $e_status) = $charset->get_decode_handle
567            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
568             byte_buffer => \ $buffer->{buffer});
569        
570        if ($char_stream) { # if supported
571          ## "Change the encoding" algorithm:
572    
573          ## Step 1    
574          if ($charset->{category} &
575              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
576            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
577            ($char_stream, $e_status) = $charset->get_decode_handle
578                ($byte_stream,
579                 byte_buffer => \ $buffer->{buffer});
580          }
581          $charset_name = $charset->get_iana_name;
582          
583          ## Step 2
584          if (defined $self->{input_encoding} and
585              $self->{input_encoding} eq $charset_name) {
586            !!!parse-error (type => 'charset label:matching',
587                            text => $charset_name,
588                            level => $self->{level}->{info});
589            $self->{confident} = 1;
590            return;
591          }
592    
593          !!!parse-error (type => 'charset label detected',
594                          text => $self->{input_encoding},
595                          value => $charset_name,
596                          level => $self->{level}->{warn},
597                          token => $token);
598          
599          ## Step 3
600          # if (can) {
601            ## change the encoding on the fly.
602            #$self->{confident} = 1;
603            #return;
604          # }
605          
606          ## Step 4
607          throw Whatpm::HTML::RestartParser ();
608        }
609    }; # $self->{change_encoding}    }; # $self->{change_encoding}
610    
611    my @args = @_; shift @args; # $s    my $char_onerror = sub {
612        my (undef, $type, %opt) = @_;
613        !!!parse-error (layer => 'encode',
614                        line => $self->{line}, column => $self->{column} + 1,
615                        %opt, type => $type);
616        if ($opt{octets}) {
617          ${$opt{octets}} = "\x{FFFD}"; # relacement character
618        }
619      };
620    
621      my $wrapped_char_stream = $get_wrapper->($char_stream);
622      $wrapped_char_stream->onerror ($char_onerror);
623    
624      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
625    my $return;    my $return;
626    try {    try {
627      $return = $self->parse_char_string ($s, @args);        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
628    } catch Whatpm::HTML::RestartParser with {    } catch Whatpm::HTML::RestartParser with {
629      my $charset = shift->{charset};      ## NOTE: Invoked after {change_encoding}.
630      $s = \ (Encode::decode ($charset, $$bytes_s));      
631      $self->{input_encoding} = $charset; ## TODO: normalize      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
632          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
633          !!!parse-error (type => 'chardecode:fallback',
634                          level => $self->{level}->{uncertain},
635                          #text => $self->{input_encoding},
636                          line => 1, column => 1,
637                          layer => 'encode');
638        } elsif (not ($e_status &
639                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
640          $self->{input_encoding} = $charset->get_iana_name;
641          !!!parse-error (type => 'chardecode:no error',
642                          text => $self->{input_encoding},
643                          level => $self->{level}->{uncertain},
644                          line => 1, column => 1,
645                          layer => 'encode');
646        } else {
647          $self->{input_encoding} = $charset->get_iana_name;
648        }
649      $self->{confident} = 1;      $self->{confident} = 1;
650      $return = $self->parse_char_string ($s, @args);  
651        $wrapped_char_stream = $get_wrapper->($char_stream);
652        $wrapped_char_stream->onerror ($char_onerror);
653    
654        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
655    };    };
656    return $return;    return $return;
657  } # parse_byte_string  } # parse_byte_stream
658    
659  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM  ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
660  ## 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 665  sub parse_byte_string ($$$$;$) {
665  ## 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
666  ## strip the BOM and never strip any ZWNBSP.  ## strip the BOM and never strip any ZWNBSP.
667    
668  *parse_char_string = \&parse_string;  sub parse_char_string ($$$;$$) {
669      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
670      my $self = shift;
671      my $s = ref $_[0] ? $_[0] : \($_[0]);
672      require Whatpm::Charset::DecodeHandle;
673      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
674      return $self->parse_char_stream ($input, @_[1..$#_]);
675    } # parse_char_string
676    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
677    
678  sub parse_string ($$$;$) {  sub parse_char_stream ($$$;$$) {
679    my $self = ref $_[0] ? shift : shift->new;    my $self = ref $_[0] ? shift : shift->new;
680    my $s = ref $_[0] ? $_[0] : \($_[0]);    my $input = $_[0];
681    $self->{document} = $_[1];    $self->{document} = $_[1];
682    @{$self->{document}->child_nodes} = ();    @{$self->{document}->child_nodes} = ();
683    
# Line 175  sub parse_string ($$$;$) { Line 686  sub parse_string ($$$;$) {
686    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
687    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
688        if defined $self->{input_encoding};        if defined $self->{input_encoding};
689    ## TODO: |{input_encoding}| is needless?
690    
691    my $i = 0;    $self->{line_prev} = $self->{line} = 1;
692    my $line = 1;    $self->{column_prev} = -1;
693    my $column = 0;    $self->{column} = 0;
694    $self->{set_next_input_character} = sub {    $self->{set_nc} = sub {
695      my $self = shift;      my $self = shift;
696    
697      pop @{$self->{prev_input_character}};      my $char = '';
698      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
699          $char = $self->{next_nc};
700          delete $self->{next_nc};
701          $self->{nc} = ord $char;
702        } else {
703          $self->{char_buffer} = '';
704          $self->{char_buffer_pos} = 0;
705    
706          my $count = $input->manakai_read_until
707             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
708          if ($count) {
709            $self->{line_prev} = $self->{line};
710            $self->{column_prev} = $self->{column};
711            $self->{column}++;
712            $self->{nc}
713                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
714            return;
715          }
716    
717      $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
718      $self->{next_input_character} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
719      $column++;        } else {
720            $self->{nc} = -1;
721            return;
722          }
723        }
724    
725        ($self->{line_prev}, $self->{column_prev})
726            = ($self->{line}, $self->{column});
727        $self->{column}++;
728            
729      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
730        $line++;        !!!cp ('j1');
731        $column = 0;        $self->{line}++;
732      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
733        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
734        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
735        $line++;  ## TODO: support for abort/streaming
736        $column = 0;        my $next = '';
737      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
738        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
739      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
740          $self->{nc} = 0x000A; # LF # MUST
741          $self->{line}++;
742          $self->{column} = 0;
743        } elsif ($self->{nc} == 0x0000) { # NULL
744          !!!cp ('j4');
745        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
746        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
747      }      }
748    };    };
749    $self->{prev_input_character} = [-1, -1, -1];  
750    $self->{next_input_character} = -1;    $self->{read_until} = sub {
751        #my ($scalar, $specials_range, $offset) = @_;
752        return 0 if defined $self->{next_nc};
753    
754        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
755        my $offset = $_[2] || 0;
756    
757        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
758          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
759          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
760            substr ($_[0], $offset)
761                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
762            my $count = $+[0] - $-[0];
763            if ($count) {
764              $self->{column} += $count;
765              $self->{char_buffer_pos} += $count;
766              $self->{line_prev} = $self->{line};
767              $self->{column_prev} = $self->{column} - 1;
768              $self->{nc} = -1;
769            }
770            return $count;
771          } else {
772            return 0;
773          }
774        } else {
775          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
776          if ($count) {
777            $self->{column} += $count;
778            $self->{line_prev} = $self->{line};
779            $self->{column_prev} = $self->{column} - 1;
780            $self->{nc} = -1;
781          }
782          return $count;
783        }
784      }; # $self->{read_until}
785    
786    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
787      my (%opt) = @_;      my (%opt) = @_;
788      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
789        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
790        warn "Parse error ($opt{type}) at line $line column $column\n";
791    };    };
792    $self->{parse_error} = sub {    $self->{parse_error} = sub {
793      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
794    };    };
795    
796      my $char_onerror = sub {
797        my (undef, $type, %opt) = @_;
798        !!!parse-error (layer => 'encode',
799                        line => $self->{line}, column => $self->{column} + 1,
800                        %opt, type => $type);
801      }; # $char_onerror
802    
803      if ($_[3]) {
804        $input = $_[3]->($input);
805        $input->onerror ($char_onerror);
806      } else {
807        $input->onerror ($char_onerror) unless defined $input->onerror;
808      }
809    
810    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
811    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
812    $self->_construct_tree;    $self->_construct_tree;
813    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
814    
815      delete $self->{parse_error}; # remove loop
816    
817    return $self->{document};    return $self->{document};
818  } # parse_string  } # parse_char_stream
819    
820  sub new ($) {  sub new ($) {
821    my $class = shift;    my $class = shift;
822    my $self = bless {}, $class;    my $self = bless {
823    $self->{set_next_input_character} = sub {      level => {must => 'm',
824      $self->{next_input_character} = -1;                should => 's',
825                  warn => 'w',
826                  info => 'i',
827                  uncertain => 'u'},
828      }, $class;
829      $self->{set_nc} = sub {
830        $self->{nc} = -1;
831    };    };
832    $self->{parse_error} = sub {    $self->{parse_error} = sub {
833      #      #
# Line 254  sub RCDATA_CONTENT_MODEL () { CM_ENTITY Line 854  sub RCDATA_CONTENT_MODEL () { CM_ENTITY
854  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
855    
856  sub DATA_STATE () { 0 }  sub DATA_STATE () { 0 }
857  sub ENTITY_DATA_STATE () { 1 }  #sub ENTITY_DATA_STATE () { 1 }
858  sub TAG_OPEN_STATE () { 2 }  sub TAG_OPEN_STATE () { 2 }
859  sub CLOSE_TAG_OPEN_STATE () { 3 }  sub CLOSE_TAG_OPEN_STATE () { 3 }
860  sub TAG_NAME_STATE () { 4 }  sub TAG_NAME_STATE () { 4 }
# Line 265  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 Line 865  sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8
865  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
866  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
867  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
868  sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
869  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
870  sub COMMENT_START_STATE () { 14 }  sub COMMENT_START_STATE () { 14 }
871  sub COMMENT_START_DASH_STATE () { 15 }  sub COMMENT_START_DASH_STATE () { 15 }
# Line 287  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO Line 887  sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUO
887  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
888  sub BOGUS_DOCTYPE_STATE () { 32 }  sub BOGUS_DOCTYPE_STATE () { 32 }
889  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
890    sub SELF_CLOSING_START_TAG_STATE () { 34 }
891    sub CDATA_SECTION_STATE () { 35 }
892    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
893    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
894    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
895    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
896    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
897    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
898    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
899    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
900    ## NOTE: "Entity data state", "entity in attribute value state", and
901    ## "consume a character reference" algorithm are jointly implemented
902    ## using the following six states:
903    sub ENTITY_STATE () { 44 }
904    sub ENTITY_HASH_STATE () { 45 }
905    sub NCR_NUM_STATE () { 46 }
906    sub HEXREF_X_STATE () { 47 }
907    sub HEXREF_HEX_STATE () { 48 }
908    sub ENTITY_NAME_STATE () { 49 }
909    sub PCDATA_STATE () { 50 } # "data state" in the spec
910    
911  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
912  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
# Line 303  sub TABLE_IMS ()      { 0b1000000 } Line 923  sub TABLE_IMS ()      { 0b1000000 }
923  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
924  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
925  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
926    sub SELECT_IMS ()     { 0b10000000000 }
927    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
928        ## NOTE: "in foreign content" insertion mode is special; it is combined
929        ## with the secondary insertion mode.  In this parser, they are stored
930        ## together in the bit-or'ed form.
931    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
932        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
933        ## combined with the original insertion mode.  In thie parser,
934        ## they are stored together in the bit-or'ed form.
935    
936    ## NOTE: "initial" and "before html" insertion modes have no constants.
937    
938    ## NOTE: "after after body" insertion mode.
939  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
940    
941    ## NOTE: "after after frameset" insertion mode.
942  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
943    
944  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
945  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
946  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 319  sub IN_TABLE_IM () { TABLE_IMS } Line 954  sub IN_TABLE_IM () { TABLE_IMS }
954  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
955  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
956  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
957  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
958    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
959  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
960    
961  ## 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 963  sub IN_COLUMN_GROUP_IM () { 0b10 }
963  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
964    my $self = shift;    my $self = shift;
965    $self->{state} = DATA_STATE; # MUST    $self->{state} = DATA_STATE; # MUST
966      #$self->{s_kwd}; # state keyword - initialized when used
967      #$self->{entity__value}; # initialized when used
968      #$self->{entity__match}; # initialized when used
969    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
970    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
971    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
972    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
973    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
974    $self->{char} = [];    delete $self->{self_closing};
975    # $self->{next_input_character}    $self->{char_buffer} = '';
976      $self->{char_buffer_pos} = 0;
977      $self->{nc} = -1; # next input character
978      #$self->{next_nc}
979    !!!next-input-character;    !!!next-input-character;
980    $self->{token} = [];    $self->{token} = [];
981    # $self->{escape}    # $self->{escape}
# Line 344  sub _initialize_tokenizer ($) { Line 986  sub _initialize_tokenizer ($) {
986  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
987  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
988  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
989  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
990  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
991  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
992  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
993  ##        ->{name}  ##        ->{name}
994  ##        ->{value}  ##        ->{value}
995  ##        ->{has_reference} == 1 or 0  ##        ->{has_reference} == 1 or 0
996  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
997    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
998    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
999    ##     while the token is pushed back to the stack.
1000    
1001  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
1002    
# Line 361  sub _initialize_tokenizer ($) { Line 1006  sub _initialize_tokenizer ($) {
1006  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1007  ## and removed from the list.  ## and removed from the list.
1008    
1009  ## NOTE: HTML5 "Writing HTML documents" section, applied to  ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1010  ## documents and not to user agents and conformance checkers,  ## (This requirement was dropped from HTML5 spec, unfortunately.)
1011  ## contains some requirements that are not detected by the  
1012  ## parsing algorithm:  my $is_space = {
1013  ## - Some requirements on character encoding declarations. ## TODO    0x0009 => 1, # CHARACTER TABULATION (HT)
1014  ## - "Elements MUST NOT contain content that their content model disallows."    0x000A => 1, # LINE FEED (LF)
1015  ##   ... Some are parse error, some are not (will be reported by c.c.).    #0x000B => 0, # LINE TABULATION (VT)
1016  ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO    0x000C => 1, # FORM FEED (FF)
1017  ## - Text (in elements, attributes, and comments) SHOULD NOT contain    #0x000D => 1, # CARRIAGE RETURN (CR)
1018  ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)    0x0020 => 1, # SPACE (SP)
1019    };
 ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot  
 ## be detected by the HTML5 parsing algorithm:  
 ## - Text,  
1020    
1021  sub _get_next_token ($) {  sub _get_next_token ($) {
1022    my $self = shift;    my $self = shift;
1023    
1024      if ($self->{self_closing}) {
1025        !!!parse-error (type => 'nestc', token => $self->{ct});
1026        ## NOTE: The |self_closing| flag is only set by start tag token.
1027        ## In addition, when a start tag token is emitted, it is always set to
1028        ## |ct|.
1029        delete $self->{self_closing};
1030      }
1031    
1032    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1033        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1034      return shift @{$self->{token}};      return shift @{$self->{token}};
1035    }    }
1036    
1037    A: {    A: {
1038      if ($self->{state} == DATA_STATE) {      if ($self->{state} == PCDATA_STATE) {
1039        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1040    
1041          if ($self->{nc} == 0x0026) { # &
1042            !!!cp (0.1);
1043            ## NOTE: In the spec, the tokenizer is switched to the
1044            ## "entity data state".  In this implementation, the tokenizer
1045            ## is switched to the |ENTITY_STATE|, which is an implementation
1046            ## of the "consume a character reference" algorithm.
1047            $self->{entity_add} = -1;
1048            $self->{prev_state} = DATA_STATE;
1049            $self->{state} = ENTITY_STATE;
1050            !!!next-input-character;
1051            redo A;
1052          } elsif ($self->{nc} == 0x003C) { # <
1053            !!!cp (0.2);
1054            $self->{state} = TAG_OPEN_STATE;
1055            !!!next-input-character;
1056            redo A;
1057          } elsif ($self->{nc} == -1) {
1058            !!!cp (0.3);
1059            !!!emit ({type => END_OF_FILE_TOKEN,
1060                      line => $self->{line}, column => $self->{column}});
1061            last A; ## TODO: ok?
1062          } else {
1063            !!!cp (0.4);
1064            #
1065          }
1066    
1067          # Anything else
1068          my $token = {type => CHARACTER_TOKEN,
1069                       data => chr $self->{nc},
1070                       line => $self->{line}, column => $self->{column},
1071                      };
1072          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1073    
1074          ## Stay in the state.
1075          !!!next-input-character;
1076          !!!emit ($token);
1077          redo A;
1078        } elsif ($self->{state} == DATA_STATE) {
1079          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1080          if ($self->{nc} == 0x0026) { # &
1081            $self->{s_kwd} = '';
1082          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1083              not $self->{escape}) {              not $self->{escape}) {
1084            $self->{state} = ENTITY_DATA_STATE;            !!!cp (1);
1085              ## NOTE: In the spec, the tokenizer is switched to the
1086              ## "entity data state".  In this implementation, the tokenizer
1087              ## is switched to the |ENTITY_STATE|, which is an implementation
1088              ## of the "consume a character reference" algorithm.
1089              $self->{entity_add} = -1;
1090              $self->{prev_state} = DATA_STATE;
1091              $self->{state} = ENTITY_STATE;
1092            !!!next-input-character;            !!!next-input-character;
1093            redo A;            redo A;
1094          } else {          } else {
1095              !!!cp (2);
1096            #            #
1097          }          }
1098        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1099          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1100            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1101              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1102                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1103                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1104                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1105              }              $self->{s_kwd} = '--';
1106                #
1107              } elsif ($self->{s_kwd} eq '---') {
1108                !!!cp (4);
1109                $self->{s_kwd} = '--';
1110                #
1111              } else {
1112                !!!cp (5);
1113                #
1114            }            }
1115          }          }
1116                    
1117          #          #
1118        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1119            if (length $self->{s_kwd}) {
1120              !!!cp (5.1);
1121              $self->{s_kwd} .= '!';
1122              #
1123            } else {
1124              !!!cp (5.2);
1125              #$self->{s_kwd} = '';
1126              #
1127            }
1128            #
1129          } elsif ($self->{nc} == 0x003C) { # <
1130          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1131              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1132               not $self->{escape})) {               not $self->{escape})) {
1133              !!!cp (6);
1134            $self->{state} = TAG_OPEN_STATE;            $self->{state} = TAG_OPEN_STATE;
1135            !!!next-input-character;            !!!next-input-character;
1136            redo A;            redo A;
1137          } else {          } else {
1138              !!!cp (7);
1139              $self->{s_kwd} = '';
1140            #            #
1141          }          }
1142        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1143          if ($self->{escape} and          if ($self->{escape} and
1144              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1145            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1146                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1147              delete $self->{escape};              delete $self->{escape};
1148              } else {
1149                !!!cp (9);
1150            }            }
1151            } else {
1152              !!!cp (10);
1153          }          }
1154                    
1155            $self->{s_kwd} = '';
1156          #          #
1157        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1158          !!!emit ({type => END_OF_FILE_TOKEN});          !!!cp (11);
1159            $self->{s_kwd} = '';
1160            !!!emit ({type => END_OF_FILE_TOKEN,
1161                      line => $self->{line}, column => $self->{column}});
1162          last A; ## TODO: ok?          last A; ## TODO: ok?
1163          } else {
1164            !!!cp (12);
1165            $self->{s_kwd} = '';
1166            #
1167        }        }
1168    
1169        # Anything else        # Anything else
1170        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1171                     data => chr $self->{next_input_character}};                     data => chr $self->{nc},
1172        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1173        !!!next-input-character;                    };
1174          if ($self->{read_until}->($token->{data}, q[-!<>&],
1175        !!!emit ($token);                                  length $token->{data})) {
1176            $self->{s_kwd} = '';
1177        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  
1178    
1179        unless (defined $token) {        ## Stay in the data state.
1180          !!!emit ({type => CHARACTER_TOKEN, data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1181            !!!cp (13);
1182            $self->{state} = PCDATA_STATE;
1183        } else {        } else {
1184          !!!emit ($token);          !!!cp (14);
1185            ## Stay in the state.
1186        }        }
1187          !!!next-input-character;
1188          !!!emit ($token);
1189        redo A;        redo A;
1190      } elsif ($self->{state} == TAG_OPEN_STATE) {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1191        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1192          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1193              !!!cp (15);
1194            !!!next-input-character;            !!!next-input-character;
1195            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1196            redo A;            redo A;
1197            } elsif ($self->{nc} == 0x0021) { # !
1198              !!!cp (15.1);
1199              $self->{s_kwd} = '<' unless $self->{escape};
1200              #
1201          } else {          } else {
1202            ## reconsume            !!!cp (16);
1203            $self->{state} = DATA_STATE;            #
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1204          }          }
1205    
1206            ## reconsume
1207            $self->{state} = DATA_STATE;
1208            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1209                      line => $self->{line_prev},
1210                      column => $self->{column_prev},
1211                     });
1212            redo A;
1213        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1214          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1215              !!!cp (17);
1216            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;            $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1217            !!!next-input-character;            !!!next-input-character;
1218            redo A;            redo A;
1219          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1220              !!!cp (18);
1221            $self->{state} = CLOSE_TAG_OPEN_STATE;            $self->{state} = CLOSE_TAG_OPEN_STATE;
1222            !!!next-input-character;            !!!next-input-character;
1223            redo A;            redo A;
1224          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1225                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1226            $self->{current_token}            !!!cp (19);
1227              $self->{ct}
1228              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1229                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
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 (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1236                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1237            $self->{current_token} = {type => START_TAG_TOKEN,            !!!cp (20);
1238                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1239                                        tag_name => chr ($self->{nc}),
1240                                        line => $self->{line_prev},
1241                                        column => $self->{column_prev}};
1242            $self->{state} = TAG_NAME_STATE;            $self->{state} = TAG_NAME_STATE;
1243            !!!next-input-character;            !!!next-input-character;
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1246            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1247              !!!parse-error (type => 'empty start tag',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1251            !!!next-input-character;            !!!next-input-character;
1252    
1253            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1254                        line => $self->{line_prev},
1255                        column => $self->{column_prev},
1256                       });
1257    
1258            redo A;            redo A;
1259          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1260            !!!parse-error (type => 'pio');            !!!cp (22);
1261              !!!parse-error (type => 'pio',
1262                              line => $self->{line_prev},
1263                              column => $self->{column_prev});
1264            $self->{state} = BOGUS_COMMENT_STATE;            $self->{state} = BOGUS_COMMENT_STATE;
1265            ## $self->{next_input_character} is intentionally left as is            $self->{ct} = {type => COMMENT_TOKEN, data => '',
1266                                        line => $self->{line_prev},
1267                                        column => $self->{column_prev},
1268                                       };
1269              ## $self->{nc} is intentionally left as is
1270            redo A;            redo A;
1271          } else {          } else {
1272            !!!parse-error (type => 'bare stago');            !!!cp (23);
1273              !!!parse-error (type => 'bare stago',
1274                              line => $self->{line_prev},
1275                              column => $self->{column_prev});
1276            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1277            ## reconsume            ## reconsume
1278    
1279            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1280                        line => $self->{line_prev},
1281                        column => $self->{column_prev},
1282                       });
1283    
1284            redo A;            redo A;
1285          }          }
# Line 517  sub _get_next_token ($) { Line 1287  sub _get_next_token ($) {
1287          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1288        }        }
1289      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1290        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1291          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = DATA_STATE;  
1292    
1293                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1294            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1295                redo A;          if (defined $self->{last_stag_name}) {
1296              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1297            }            $self->{s_kwd} = '';
1298            push @next_char, $self->{next_input_character};            ## Reconsume.
1299                    redo A;
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = DATA_STATE;  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1300          } else {          } else {
1301            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1302            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1303              !!!cp (28);
1304            $self->{state} = DATA_STATE;            $self->{state} = DATA_STATE;
1305            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            ## Reconsume.
1306              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1307                        line => $l, column => $c,
1308                       });
1309            redo A;            redo A;
1310          }          }
1311        }        }
1312          
1313        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1314            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1315          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (29);
1316                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1317                = {type => END_TAG_TOKEN,
1318                   tag_name => chr ($self->{nc} + 0x0020),
1319                   line => $l, column => $c};
1320          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1321          !!!next-input-character;          !!!next-input-character;
1322          redo A;          redo A;
1323        } elsif (0x0061 <= $self->{next_input_character} and        } elsif (0x0061 <= $self->{nc} and
1324                 $self->{next_input_character} <= 0x007A) { # a..z                 $self->{nc} <= 0x007A) { # a..z
1325          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (30);
1326                            tag_name => chr ($self->{next_input_character})};          $self->{ct} = {type => END_TAG_TOKEN,
1327                                      tag_name => chr ($self->{nc}),
1328                                      line => $l, column => $c};
1329          $self->{state} = TAG_NAME_STATE;          $self->{state} = TAG_NAME_STATE;
1330          !!!next-input-character;          !!!next-input-character;
1331          redo A;          redo A;
1332        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1333          !!!parse-error (type => 'empty end tag');          !!!cp (31);
1334            !!!parse-error (type => 'empty end tag',
1335                            line => $self->{line_prev}, ## "<" in "</>"
1336                            column => $self->{column_prev} - 1);
1337          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1338          !!!next-input-character;          !!!next-input-character;
1339          redo A;          redo A;
1340        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1341            !!!cp (32);
1342          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1343          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1344          # reconsume          # reconsume
1345    
1346          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1347                      line => $l, column => $c,
1348                     });
1349    
1350          redo A;          redo A;
1351        } else {        } else {
1352            !!!cp (33);
1353          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1354          $self->{state} = BOGUS_COMMENT_STATE;          $self->{state} = BOGUS_COMMENT_STATE;
1355          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1356          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1357                                      column => $self->{column_prev} - 1,
1358                                     };
1359            ## NOTE: $self->{nc} is intentionally left as is.
1360            ## Although the "anything else" case of the spec not explicitly
1361            ## states that the next input character is to be reconsumed,
1362            ## it will be included to the |data| of the comment token
1363            ## generated from the bogus end tag, as defined in the
1364            ## "bogus comment state" entry.
1365            redo A;
1366          }
1367        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1368          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1369          if (length $ch) {
1370            my $CH = $ch;
1371            $ch =~ tr/a-z/A-Z/;
1372            my $nch = chr $self->{nc};
1373            if ($nch eq $ch or $nch eq $CH) {
1374              !!!cp (24);
1375              ## Stay in the state.
1376              $self->{s_kwd} .= $nch;
1377              !!!next-input-character;
1378              redo A;
1379            } else {
1380              !!!cp (25);
1381              $self->{state} = DATA_STATE;
1382              ## Reconsume.
1383              !!!emit ({type => CHARACTER_TOKEN,
1384                        data => '</' . $self->{s_kwd},
1385                        line => $self->{line_prev},
1386                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1387                       });
1388              redo A;
1389            }
1390          } else { # after "<{tag-name}"
1391            unless ($is_space->{$self->{nc}} or
1392                    {
1393                     0x003E => 1, # >
1394                     0x002F => 1, # /
1395                     -1 => 1, # EOF
1396                    }->{$self->{nc}}) {
1397              !!!cp (26);
1398              ## Reconsume.
1399              $self->{state} = DATA_STATE;
1400              !!!emit ({type => CHARACTER_TOKEN,
1401                        data => '</' . $self->{s_kwd},
1402                        line => $self->{line_prev},
1403                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1404                       });
1405              redo A;
1406            } else {
1407              !!!cp (27);
1408              $self->{ct}
1409                  = {type => END_TAG_TOKEN,
1410                     tag_name => $self->{last_stag_name},
1411                     line => $self->{line_prev},
1412                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1413              $self->{state} = TAG_NAME_STATE;
1414              ## Reconsume.
1415              redo A;
1416            }
1417        }        }
1418      } elsif ($self->{state} == TAG_NAME_STATE) {      } elsif ($self->{state} == TAG_NAME_STATE) {
1419        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1420            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1421          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1422          !!!next-input-character;          !!!next-input-character;
1423          redo A;          redo A;
1424        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1425          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426            $self->{current_token}->{first_start_tag}            !!!cp (35);
1427                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1429            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1431              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1432            }            #  !!! cp (36);
1433              #  !!! parse-error (type => 'end tag attribute');
1434              #} else {
1435                !!!cp (37);
1436              #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1441          !!!next-input-character;          !!!next-input-character;
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1447                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1448          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1449            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1450            # start tag or end tag            # start tag or end tag
1451          ## Stay in this state          ## Stay in this state
1452          !!!next-input-character;          !!!next-input-character;
1453          redo A;          redo A;
1454        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1455          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1456          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1457            $self->{current_token}->{first_start_tag}            !!!cp (39);
1458                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1459            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1460            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1461            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1462              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1463            }            #  !!! cp (40);
1464              #  !!! parse-error (type => 'end tag attribute');
1465              #} else {
1466                !!!cp (41);
1467              #}
1468          } else {          } else {
1469            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1470          }          }
1471          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1472          # reconsume          # reconsume
1473    
1474          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1475    
1476          redo A;          redo A;
1477        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1478            !!!cp (42);
1479            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1480          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1481          redo A;          redo A;
1482        } else {        } else {
1483          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1484            $self->{ct}->{tag_name} .= chr $self->{nc};
1485            # start tag or end tag            # start tag or end tag
1486          ## Stay in the state          ## Stay in the state
1487          !!!next-input-character;          !!!next-input-character;
1488          redo A;          redo A;
1489        }        }
1490      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1491        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1492            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1493          ## Stay in the state          ## Stay in the state
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1497          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1498            $self->{current_token}->{first_start_tag}            !!!cp (46);
1499                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1500            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1501            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1502            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1503                !!!cp (47);
1504              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1505              } else {
1506                !!!cp (48);
1507            }            }
1508          } else {          } else {
1509            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1510          }          }
1511          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1512          !!!next-input-character;          !!!next-input-character;
1513    
1514          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1515    
1516          redo A;          redo A;
1517        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1518                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1519          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1520                                value => ''};          $self->{ca}
1521                = {name => chr ($self->{nc} + 0x0020),
1522                   value => '',
1523                   line => $self->{line}, column => $self->{column}};
1524          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1525          !!!next-input-character;          !!!next-input-character;
1526          redo A;          redo A;
1527        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1528            !!!cp (50);
1529            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1530          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1531          redo A;          redo A;
1532        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1533          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1534          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1535            $self->{current_token}->{first_start_tag}            !!!cp (52);
1536                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1537            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1538            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1539            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1540                !!!cp (53);
1541              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1542              } else {
1543                !!!cp (54);
1544            }            }
1545          } else {          } else {
1546            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1547          }          }
1548          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1549          # reconsume          # reconsume
1550    
1551          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1552    
1553          redo A;          redo A;
1554        } else {        } else {
# Line 748  sub _get_next_token ($) { Line 1556  sub _get_next_token ($) {
1556               0x0022 => 1, # "               0x0022 => 1, # "
1557               0x0027 => 1, # '               0x0027 => 1, # '
1558               0x003D => 1, # =               0x003D => 1, # =
1559              }->{$self->{next_input_character}}) {              }->{$self->{nc}}) {
1560              !!!cp (55);
1561            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1562            } else {
1563              !!!cp (56);
1564          }          }
1565          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          $self->{ca}
1566                                value => ''};              = {name => chr ($self->{nc}),
1567                   value => '',
1568                   line => $self->{line}, column => $self->{column}};
1569          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1570          !!!next-input-character;          !!!next-input-character;
1571          redo A;          redo A;
1572        }        }
1573      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1574        my $before_leave = sub {        my $before_leave = sub {
1575          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1576              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1577            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1578            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1579          } else {            ## Discard $self->{ca} # MUST
1580            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1581              = $self->{current_attribute};            !!!cp (58);
1582              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1583                = $self->{ca};
1584          }          }
1585        }; # $before_leave        }; # $before_leave
1586    
1587        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1588            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1594            !!!cp (60);
1595          $before_leave->();          $before_leave->();
1596          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1597          !!!next-input-character;          !!!next-input-character;
1598          redo A;          redo A;
1599        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1600          $before_leave->();          $before_leave->();
1601          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1602            $self->{current_token}->{first_start_tag}            !!!cp (61);
1603                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1604            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1605          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {            !!!cp (62);
1606            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1607            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1608              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1609            }            }
1610          } else {          } else {
1611            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1612          }          }
1613          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1614          !!!next-input-character;          !!!next-input-character;
1615    
1616          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1617    
1618          redo A;          redo A;
1619        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1620                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1621          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1622            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1623          ## Stay in the state          ## Stay in the state
1624          !!!next-input-character;          !!!next-input-character;
1625          redo A;          redo A;
1626        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1627            !!!cp (64);
1628          $before_leave->();          $before_leave->();
1629            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1630          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1631          redo A;          redo A;
1632        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1633          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1634          $before_leave->();          $before_leave->();
1635          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1636            $self->{current_token}->{first_start_tag}            !!!cp (66);
1637                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1638            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1639            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1640            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1641                !!!cp (67);
1642              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1643              } else {
1644                ## NOTE: This state should never be reached.
1645                !!!cp (68);
1646            }            }
1647          } else {          } else {
1648            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1649          }          }
1650          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1651          # reconsume          # reconsume
1652    
1653          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1654    
1655          redo A;          redo A;
1656        } else {        } else {
1657          if ($self->{next_input_character} == 0x0022 or # "          if ($self->{nc} == 0x0022 or # "
1658              $self->{next_input_character} == 0x0027) { # '              $self->{nc} == 0x0027) { # '
1659              !!!cp (69);
1660            !!!parse-error (type => 'bad attribute name');            !!!parse-error (type => 'bad attribute name');
1661            } else {
1662              !!!cp (70);
1663          }          }
1664          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          $self->{ca}->{name} .= chr ($self->{nc});
1665          ## Stay in the state          ## Stay in the state
1666          !!!next-input-character;          !!!next-input-character;
1667          redo A;          redo A;
1668        }        }
1669      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1670        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1671            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1672          ## Stay in the state          ## Stay in the state
1673          !!!next-input-character;          !!!next-input-character;
1674          redo A;          redo A;
1675        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1676            !!!cp (72);
1677          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1678          !!!next-input-character;          !!!next-input-character;
1679          redo A;          redo A;
1680        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1681          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1682            $self->{current_token}->{first_start_tag}            !!!cp (73);
1683                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1684            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1685            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1686            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1687                !!!cp (74);
1688              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1689              } else {
1690                ## NOTE: This state should never be reached.
1691                !!!cp (75);
1692            }            }
1693          } else {          } else {
1694            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1695          }          }
1696          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1697          !!!next-input-character;          !!!next-input-character;
1698    
1699          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1700    
1701          redo A;          redo A;
1702        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1703                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1704          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1705                                value => ''};          $self->{ca}
1706                = {name => chr ($self->{nc} + 0x0020),
1707                   value => '',
1708                   line => $self->{line}, column => $self->{column}};
1709          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1710          !!!next-input-character;          !!!next-input-character;
1711          redo A;          redo A;
1712        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1713            !!!cp (77);
1714            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1715          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} == START_TAG_TOKEN and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         # next-input-character is already done  
1716          redo A;          redo A;
1717        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1718          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1719          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1720            $self->{current_token}->{first_start_tag}            !!!cp (79);
1721                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1722            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1723            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1724            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1725                !!!cp (80);
1726              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1727              } else {
1728                ## NOTE: This state should never be reached.
1729                !!!cp (81);
1730            }            }
1731          } else {          } else {
1732            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1733          }          }
1734          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1735          # reconsume          # reconsume
1736    
1737          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1738    
1739          redo A;          redo A;
1740        } else {        } else {
1741          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1742                                value => ''};              $self->{nc} == 0x0027) { # '
1743              !!!cp (78);
1744              !!!parse-error (type => 'bad attribute name');
1745            } else {
1746              !!!cp (82);
1747            }
1748            $self->{ca}
1749                = {name => chr ($self->{nc}),
1750                   value => '',
1751                   line => $self->{line}, column => $self->{column}};
1752          $self->{state} = ATTRIBUTE_NAME_STATE;          $self->{state} = ATTRIBUTE_NAME_STATE;
1753          !!!next-input-character;          !!!next-input-character;
1754          redo A;                  redo A;        
1755        }        }
1756      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1757        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1758            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1759          ## Stay in the state          ## Stay in the state
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1763            !!!cp (84);
1764          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1765          !!!next-input-character;          !!!next-input-character;
1766          redo A;          redo A;
1767        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1768            !!!cp (85);
1769          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1770          ## reconsume          ## reconsume
1771          redo A;          redo A;
1772        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1773            !!!cp (86);
1774          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1775          !!!next-input-character;          !!!next-input-character;
1776          redo A;          redo A;
1777        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1778          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1779            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1780                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1781            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1782          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1783            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1784            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1785                !!!cp (88);
1786              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1787              } else {
1788                ## NOTE: This state should never be reached.
1789                !!!cp (89);
1790            }            }
1791          } else {          } else {
1792            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1793          }          }
1794          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1795          !!!next-input-character;          !!!next-input-character;
1796    
1797          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1798    
1799          redo A;          redo A;
1800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1801          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1802          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1803            $self->{current_token}->{first_start_tag}            !!!cp (90);
1804                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1805            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1806            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1807            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1808                !!!cp (91);
1809              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1810              } else {
1811                ## NOTE: This state should never be reached.
1812                !!!cp (92);
1813            }            }
1814          } else {          } else {
1815            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1816          }          }
1817          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1818          ## reconsume          ## reconsume
1819    
1820          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1821    
1822          redo A;          redo A;
1823        } else {        } else {
1824          if ($self->{next_input_character} == 0x003D) { # =          if ($self->{nc} == 0x003D) { # =
1825              !!!cp (93);
1826            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
1827            } else {
1828              !!!cp (94);
1829          }          }
1830          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{ca}->{value} .= chr ($self->{nc});
1831          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;          $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1832          !!!next-input-character;          !!!next-input-character;
1833          redo A;          redo A;
1834        }        }
1835      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1836        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1837            !!!cp (95);
1838          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1839          !!!next-input-character;          !!!next-input-character;
1840          redo A;          redo A;
1841        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1842          $self->{last_attribute_value_state} = $self->{state};          !!!cp (96);
1843          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1844            ## "entity in attribute value state".  In this implementation, the
1845            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1846            ## implementation of the "consume a character reference" algorithm.
1847            $self->{prev_state} = $self->{state};
1848            $self->{entity_add} = 0x0022; # "
1849            $self->{state} = ENTITY_STATE;
1850          !!!next-input-character;          !!!next-input-character;
1851          redo A;          redo A;
1852        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1853          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1854          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1855            $self->{current_token}->{first_start_tag}            !!!cp (97);
1856                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1857            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1858            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1859            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1860                !!!cp (98);
1861              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1862              } else {
1863                ## NOTE: This state should never be reached.
1864                !!!cp (99);
1865            }            }
1866          } else {          } else {
1867            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1868          }          }
1869          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1870          ## reconsume          ## reconsume
1871    
1872          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1873    
1874          redo A;          redo A;
1875        } else {        } else {
1876          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1877            $self->{ca}->{value} .= chr ($self->{nc});
1878            $self->{read_until}->($self->{ca}->{value},
1879                                  q["&],
1880                                  length $self->{ca}->{value});
1881    
1882          ## Stay in the state          ## Stay in the state
1883          !!!next-input-character;          !!!next-input-character;
1884          redo A;          redo A;
1885        }        }
1886      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1887        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1888            !!!cp (101);
1889          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1890          !!!next-input-character;          !!!next-input-character;
1891          redo A;          redo A;
1892        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1893          $self->{last_attribute_value_state} = $self->{state};          !!!cp (102);
1894          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1895            ## "entity in attribute value state".  In this implementation, the
1896            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1897            ## implementation of the "consume a character reference" algorithm.
1898            $self->{entity_add} = 0x0027; # '
1899            $self->{prev_state} = $self->{state};
1900            $self->{state} = ENTITY_STATE;
1901          !!!next-input-character;          !!!next-input-character;
1902          redo A;          redo A;
1903        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1904          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1905          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1906            $self->{current_token}->{first_start_tag}            !!!cp (103);
1907                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1908            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1909            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1910            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1911                !!!cp (104);
1912              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1913              } else {
1914                ## NOTE: This state should never be reached.
1915                !!!cp (105);
1916            }            }
1917          } else {          } else {
1918            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1919          }          }
1920          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1921          ## reconsume          ## reconsume
1922    
1923          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1924    
1925          redo A;          redo A;
1926        } else {        } else {
1927          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1928            $self->{ca}->{value} .= chr ($self->{nc});
1929            $self->{read_until}->($self->{ca}->{value},
1930                                  q['&],
1931                                  length $self->{ca}->{value});
1932    
1933          ## Stay in the state          ## Stay in the state
1934          !!!next-input-character;          !!!next-input-character;
1935          redo A;          redo A;
1936        }        }
1937      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1938        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1939            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
           $self->{next_input_character} == 0x000B or # HT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1940          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1941          !!!next-input-character;          !!!next-input-character;
1942          redo A;          redo A;
1943        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1944          $self->{last_attribute_value_state} = $self->{state};          !!!cp (108);
1945          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;          ## NOTE: In the spec, the tokenizer is switched to the
1946          !!!next-input-character;          ## "entity in attribute value state".  In this implementation, the
1947          redo A;          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1948        } elsif ($self->{next_input_character} == 0x003E) { # >          ## implementation of the "consume a character reference" algorithm.
1949          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          $self->{entity_add} = -1;
1950            $self->{current_token}->{first_start_tag}          $self->{prev_state} = $self->{state};
1951                = not defined $self->{last_emitted_start_tag_name};          $self->{state} = ENTITY_STATE;
1952            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          !!!next-input-character;
1953          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          redo A;
1954          } elsif ($self->{nc} == 0x003E) { # >
1955            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1956              !!!cp (109);
1957              $self->{last_stag_name} = $self->{ct}->{tag_name};
1958            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1959            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1960            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1961                !!!cp (110);
1962              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1963              } else {
1964                ## NOTE: This state should never be reached.
1965                !!!cp (111);
1966            }            }
1967          } else {          } else {
1968            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1969          }          }
1970          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1971          !!!next-input-character;          !!!next-input-character;
1972    
1973          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1974    
1975          redo A;          redo A;
1976        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1977          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1978          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1979            $self->{current_token}->{first_start_tag}            !!!cp (112);
1980                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1981            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1982            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1983            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1984                !!!cp (113);
1985              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1986              } else {
1987                ## NOTE: This state should never be reached.
1988                !!!cp (114);
1989            }            }
1990          } else {          } else {
1991            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1992          }          }
1993          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
1994          ## reconsume          ## reconsume
1995    
1996          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1997    
1998          redo A;          redo A;
1999        } else {        } else {
# Line 1133  sub _get_next_token ($) { Line 2001  sub _get_next_token ($) {
2001               0x0022 => 1, # "               0x0022 => 1, # "
2002               0x0027 => 1, # '               0x0027 => 1, # '
2003               0x003D => 1, # =               0x003D => 1, # =
2004              }->{$self->{next_input_character}}) {              }->{$self->{nc}}) {
2005              !!!cp (115);
2006            !!!parse-error (type => 'bad attribute value');            !!!parse-error (type => 'bad attribute value');
2007            } else {
2008              !!!cp (116);
2009          }          }
2010          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          $self->{ca}->{value} .= chr ($self->{nc});
2011            $self->{read_until}->($self->{ca}->{value},
2012                                  q["'=& >],
2013                                  length $self->{ca}->{value});
2014    
2015          ## Stay in the state          ## Stay in the state
2016          !!!next-input-character;          !!!next-input-character;
2017          redo A;          redo A;
2018        }        }
     } 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) {  
         $self->{current_attribute}->{value} .= '&';  
       } else {  
         $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;  
2019      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2020        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2021            $self->{next_input_character} == 0x000A or # LF          !!!cp (118);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2022          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2023          !!!next-input-character;          !!!next-input-character;
2024          redo A;          redo A;
2025        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2026          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2027            $self->{current_token}->{first_start_tag}            !!!cp (119);
2028                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2029            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
2030            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2031            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
2032                !!!cp (120);
2033              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
2034              } else {
2035                ## NOTE: This state should never be reached.
2036                !!!cp (121);
2037            }            }
2038          } else {          } else {
2039            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2040          }          }
2041          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2042          !!!next-input-character;          !!!next-input-character;
2043    
2044          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
2045    
2046          redo A;          redo A;
2047        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
2048            !!!cp (122);
2049            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2050          !!!next-input-character;          !!!next-input-character;
2051          if ($self->{next_input_character} == 0x003E and # >          redo A;
2052              $self->{current_token}->{type} == START_TAG_TOKEN and        } elsif ($self->{nc} == -1) {
2053              $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {          !!!parse-error (type => 'unclosed tag');
2054            # permitted slash          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2055            #            !!!cp (122.3);
2056              $self->{last_stag_name} = $self->{ct}->{tag_name};
2057            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2058              if ($self->{ct}->{attributes}) {
2059                !!!cp (122.1);
2060                !!!parse-error (type => 'end tag attribute');
2061              } else {
2062                ## NOTE: This state should never be reached.
2063                !!!cp (122.2);
2064              }
2065          } else {          } else {
2066            !!!parse-error (type => 'nestc');            die "$0: $self->{ct}->{type}: Unknown token type";
2067          }          }
2068          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = DATA_STATE;
2069          # next-input-character is already done          ## Reconsume.
2070            !!!emit ($self->{ct}); # start tag or end tag
2071          redo A;          redo A;
2072        } else {        } else {
2073            !!!cp ('124.1');
2074          !!!parse-error (type => 'no space between attributes');          !!!parse-error (type => 'no space between attributes');
2075          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2076          ## reconsume          ## reconsume
2077          redo A;          redo A;
2078        }        }
2079        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2080          if ($self->{nc} == 0x003E) { # >
2081            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2082              !!!cp ('124.2');
2083              !!!parse-error (type => 'nestc', token => $self->{ct});
2084              ## TODO: Different type than slash in start tag
2085              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2086              if ($self->{ct}->{attributes}) {
2087                !!!cp ('124.4');
2088                !!!parse-error (type => 'end tag attribute');
2089              } else {
2090                !!!cp ('124.5');
2091              }
2092              ## TODO: Test |<title></title/>|
2093            } else {
2094              !!!cp ('124.3');
2095              $self->{self_closing} = 1;
2096            }
2097    
2098            $self->{state} = DATA_STATE;
2099            !!!next-input-character;
2100    
2101            !!!emit ($self->{ct}); # start tag or end tag
2102    
2103            redo A;
2104          } elsif ($self->{nc} == -1) {
2105            !!!parse-error (type => 'unclosed tag');
2106            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2107              !!!cp (124.7);
2108              $self->{last_stag_name} = $self->{ct}->{tag_name};
2109            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2110              if ($self->{ct}->{attributes}) {
2111                !!!cp (124.5);
2112                !!!parse-error (type => 'end tag attribute');
2113              } else {
2114                ## NOTE: This state should never be reached.
2115                !!!cp (124.6);
2116              }
2117            } else {
2118              die "$0: $self->{ct}->{type}: Unknown token type";
2119            }
2120            $self->{state} = DATA_STATE;
2121            ## Reconsume.
2122            !!!emit ($self->{ct}); # start tag or end tag
2123            redo A;
2124          } else {
2125            !!!cp ('124.4');
2126            !!!parse-error (type => 'nestc');
2127            ## TODO: This error type is wrong.
2128            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2129            ## Reconsume.
2130            redo A;
2131          }
2132      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {      } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2133        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
         
       my $token = {type => COMMENT_TOKEN, data => ''};  
2134    
2135        BC: {        ## NOTE: Unlike spec's "bogus comment state", this implementation
2136          if ($self->{next_input_character} == 0x003E) { # >        ## consumes characters one-by-one basis.
2137            $self->{state} = DATA_STATE;        
2138            !!!next-input-character;        if ($self->{nc} == 0x003E) { # >
2139            !!!cp (124);
2140            !!!emit ($token);          $self->{state} = DATA_STATE;
2141            !!!next-input-character;
2142    
2143            redo A;          !!!emit ($self->{ct}); # comment
2144          } elsif ($self->{next_input_character} == -1) {          redo A;
2145            $self->{state} = DATA_STATE;        } elsif ($self->{nc} == -1) {
2146            ## reconsume          !!!cp (125);
2147            $self->{state} = DATA_STATE;
2148            ## reconsume
2149    
2150            !!!emit ($token);          !!!emit ($self->{ct}); # comment
2151            redo A;
2152          } else {
2153            !!!cp (126);
2154            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2155            $self->{read_until}->($self->{ct}->{data},
2156                                  q[>],
2157                                  length $self->{ct}->{data});
2158    
2159            redo A;          ## Stay in the state.
2160          } else {          !!!next-input-character;
2161            $token->{data} .= chr ($self->{next_input_character});          redo A;
2162            !!!next-input-character;        }
           redo BC;  
         }  
       } # BC  
2163      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2164        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
   
       my @next_char;  
       push @next_char, $self->{next_input_character};  
2165                
2166        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2167            !!!cp (133);
2168            $self->{state} = MD_HYPHEN_STATE;
2169          !!!next-input-character;          !!!next-input-character;
2170          push @next_char, $self->{next_input_character};          redo A;
2171          if ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x0044 or # D
2172            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};                 $self->{nc} == 0x0064) { # d
2173            $self->{state} = COMMENT_START_STATE;          ## ASCII case-insensitive.
2174            !!!next-input-character;          !!!cp (130);
2175            redo A;          $self->{state} = MD_DOCTYPE_STATE;
2176          }          $self->{s_kwd} = chr $self->{nc};
       } elsif ($self->{next_input_character} == 0x0044 or # D  
                $self->{next_input_character} == 0x0064) { # d  
2177          !!!next-input-character;          !!!next-input-character;
2178          push @next_char, $self->{next_input_character};          redo A;
2179          if ($self->{next_input_character} == 0x004F or # O        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2180              $self->{next_input_character} == 0x006F) { # o                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2181            !!!next-input-character;                 $self->{nc} == 0x005B) { # [
2182            push @next_char, $self->{next_input_character};          !!!cp (135.4);                
2183            if ($self->{next_input_character} == 0x0043 or # C          $self->{state} = MD_CDATA_STATE;
2184                $self->{next_input_character} == 0x0063) { # c          $self->{s_kwd} = '[';
2185              !!!next-input-character;          !!!next-input-character;
2186              push @next_char, $self->{next_input_character};          redo A;
2187              if ($self->{next_input_character} == 0x0054 or # T        } else {
2188                  $self->{next_input_character} == 0x0074) { # t          !!!cp (136);
               !!!next-input-character;  
               push @next_char, $self->{next_input_character};  
               if ($self->{next_input_character} == 0x0059 or # Y  
                   $self->{next_input_character} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_input_character};  
                 if ($self->{next_input_character} == 0x0050 or # P  
                     $self->{next_input_character} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_input_character};  
                   if ($self->{next_input_character} == 0x0045 or # E  
                       $self->{next_input_character} == 0x0065) { # e  
                     ## ISSUE: What a stupid code this is!  
                     $self->{state} = DOCTYPE_STATE;  
                     !!!next-input-character;  
                     redo A;  
                   }  
                 }  
               }  
             }  
           }  
         }  
2189        }        }
2190    
2191        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2192        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2193        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2194          ## Reconsume.
2195        $self->{state} = BOGUS_COMMENT_STATE;        $self->{state} = BOGUS_COMMENT_STATE;
2196          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2197                                    line => $self->{line_prev},
2198                                    column => $self->{column_prev} - 1,
2199                                   };
2200        redo A;        redo A;
2201              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2202        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2203        ## 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);
2204            $self->{ct} = {type => COMMENT_TOKEN, data => '',
2205                                      line => $self->{line_prev},
2206                                      column => $self->{column_prev} - 2,
2207                                     };
2208            $self->{state} = COMMENT_START_STATE;
2209            !!!next-input-character;
2210            redo A;
2211          } else {
2212            !!!cp (128);
2213            !!!parse-error (type => 'bogus comment',
2214                            line => $self->{line_prev},
2215                            column => $self->{column_prev} - 2);
2216            $self->{state} = BOGUS_COMMENT_STATE;
2217            ## Reconsume.
2218            $self->{ct} = {type => COMMENT_TOKEN,
2219                                      data => '-',
2220                                      line => $self->{line_prev},
2221                                      column => $self->{column_prev} - 2,
2222                                     };
2223            redo A;
2224          }
2225        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2226          ## ASCII case-insensitive.
2227          if ($self->{nc} == [
2228                undef,
2229                0x004F, # O
2230                0x0043, # C
2231                0x0054, # T
2232                0x0059, # Y
2233                0x0050, # P
2234              ]->[length $self->{s_kwd}] or
2235              $self->{nc} == [
2236                undef,
2237                0x006F, # o
2238                0x0063, # c
2239                0x0074, # t
2240                0x0079, # y
2241                0x0070, # p
2242              ]->[length $self->{s_kwd}]) {
2243            !!!cp (131);
2244            ## Stay in the state.
2245            $self->{s_kwd} .= chr $self->{nc};
2246            !!!next-input-character;
2247            redo A;
2248          } elsif ((length $self->{s_kwd}) == 6 and
2249                   ($self->{nc} == 0x0045 or # E
2250                    $self->{nc} == 0x0065)) { # e
2251            !!!cp (129);
2252            $self->{state} = DOCTYPE_STATE;
2253            $self->{ct} = {type => DOCTYPE_TOKEN,
2254                                      quirks => 1,
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 7,
2257                                     };
2258            !!!next-input-character;
2259            redo A;
2260          } else {
2261            !!!cp (132);        
2262            !!!parse-error (type => 'bogus comment',
2263                            line => $self->{line_prev},
2264                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2265            $self->{state} = BOGUS_COMMENT_STATE;
2266            ## Reconsume.
2267            $self->{ct} = {type => COMMENT_TOKEN,
2268                                      data => $self->{s_kwd},
2269                                      line => $self->{line_prev},
2270                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2271                                     };
2272            redo A;
2273          }
2274        } elsif ($self->{state} == MD_CDATA_STATE) {
2275          if ($self->{nc} == {
2276                '[' => 0x0043, # C
2277                '[C' => 0x0044, # D
2278                '[CD' => 0x0041, # A
2279                '[CDA' => 0x0054, # T
2280                '[CDAT' => 0x0041, # A
2281              }->{$self->{s_kwd}}) {
2282            !!!cp (135.1);
2283            ## Stay in the state.
2284            $self->{s_kwd} .= chr $self->{nc};
2285            !!!next-input-character;
2286            redo A;
2287          } elsif ($self->{s_kwd} eq '[CDATA' and
2288                   $self->{nc} == 0x005B) { # [
2289            !!!cp (135.2);
2290            $self->{ct} = {type => CHARACTER_TOKEN,
2291                                      data => '',
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 7};
2294            $self->{state} = CDATA_SECTION_STATE;
2295            !!!next-input-character;
2296            redo A;
2297          } else {
2298            !!!cp (135.3);
2299            !!!parse-error (type => 'bogus comment',
2300                            line => $self->{line_prev},
2301                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2302            $self->{state} = BOGUS_COMMENT_STATE;
2303            ## Reconsume.
2304            $self->{ct} = {type => COMMENT_TOKEN,
2305                                      data => $self->{s_kwd},
2306                                      line => $self->{line_prev},
2307                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2308                                     };
2309            redo A;
2310          }
2311      } elsif ($self->{state} == COMMENT_START_STATE) {      } elsif ($self->{state} == COMMENT_START_STATE) {
2312        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2313            !!!cp (137);
2314          $self->{state} = COMMENT_START_DASH_STATE;          $self->{state} = COMMENT_START_DASH_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316          redo A;          redo A;
2317        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2318            !!!cp (138);
2319          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2320          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2321          !!!next-input-character;          !!!next-input-character;
2322    
2323          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2324    
2325          redo A;          redo A;
2326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2327            !!!cp (139);
2328          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2329          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2330          ## reconsume          ## reconsume
2331    
2332          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2333    
2334          redo A;          redo A;
2335        } else {        } else {
2336          $self->{current_token}->{data} # comment          !!!cp (140);
2337              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2338                .= chr ($self->{nc});
2339          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2340          !!!next-input-character;          !!!next-input-character;
2341          redo A;          redo A;
2342        }        }
2343      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2344        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2345            !!!cp (141);
2346          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348          redo A;          redo A;
2349        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2350            !!!cp (142);
2351          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2352          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2353          !!!next-input-character;          !!!next-input-character;
2354    
2355          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2356    
2357          redo A;          redo A;
2358        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2359            !!!cp (143);
2360          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2361          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2362          ## reconsume          ## reconsume
2363    
2364          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2365    
2366          redo A;          redo A;
2367        } else {        } else {
2368          $self->{current_token}->{data} # comment          !!!cp (144);
2369              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2370                .= '-' . chr ($self->{nc});
2371          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2372          !!!next-input-character;          !!!next-input-character;
2373          redo A;          redo A;
2374        }        }
2375      } elsif ($self->{state} == COMMENT_STATE) {      } elsif ($self->{state} == COMMENT_STATE) {
2376        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2377            !!!cp (145);
2378          $self->{state} = COMMENT_END_DASH_STATE;          $self->{state} = COMMENT_END_DASH_STATE;
2379          !!!next-input-character;          !!!next-input-character;
2380          redo A;          redo A;
2381        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2382            !!!cp (146);
2383          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2384          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2385          ## reconsume          ## reconsume
2386    
2387          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2388    
2389          redo A;          redo A;
2390        } else {        } else {
2391          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2392            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2393            $self->{read_until}->($self->{ct}->{data},
2394                                  q[-],
2395                                  length $self->{ct}->{data});
2396    
2397          ## Stay in the state          ## Stay in the state
2398          !!!next-input-character;          !!!next-input-character;
2399          redo A;          redo A;
2400        }        }
2401      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2402        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2403            !!!cp (148);
2404          $self->{state} = COMMENT_END_STATE;          $self->{state} = COMMENT_END_STATE;
2405          !!!next-input-character;          !!!next-input-character;
2406          redo A;          redo A;
2407        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2408            !!!cp (149);
2409          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2410          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2411          ## reconsume          ## reconsume
2412    
2413          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2414    
2415          redo A;          redo A;
2416        } else {        } else {
2417          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2418            $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2419          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2420          !!!next-input-character;          !!!next-input-character;
2421          redo A;          redo A;
2422        }        }
2423      } elsif ($self->{state} == COMMENT_END_STATE) {      } elsif ($self->{state} == COMMENT_END_STATE) {
2424        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2425            !!!cp (151);
2426          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2427          !!!next-input-character;          !!!next-input-character;
2428    
2429          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2430    
2431          redo A;          redo A;
2432        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2433          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2434          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2435                            line => $self->{line_prev},
2436                            column => $self->{column_prev});
2437            $self->{ct}->{data} .= '-'; # comment
2438          ## Stay in the state          ## Stay in the state
2439          !!!next-input-character;          !!!next-input-character;
2440          redo A;          redo A;
2441        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2442            !!!cp (153);
2443          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2444          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2445          ## reconsume          ## reconsume
2446    
2447          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2448    
2449          redo A;          redo A;
2450        } else {        } else {
2451          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2452          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2453                            line => $self->{line_prev},
2454                            column => $self->{column_prev});
2455            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2456          $self->{state} = COMMENT_STATE;          $self->{state} = COMMENT_STATE;
2457          !!!next-input-character;          !!!next-input-character;
2458          redo A;          redo A;
2459        }        }
2460      } elsif ($self->{state} == DOCTYPE_STATE) {      } elsif ($self->{state} == DOCTYPE_STATE) {
2461        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2462            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2463          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2464          !!!next-input-character;          !!!next-input-character;
2465          redo A;          redo A;
2466        } else {        } else {
2467            !!!cp (156);
2468          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2469          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2470          ## reconsume          ## reconsume
2471          redo A;          redo A;
2472        }        }
2473      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2474        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2475            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2476          ## Stay in the state          ## Stay in the state
2477          !!!next-input-character;          !!!next-input-character;
2478          redo A;          redo A;
2479        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2480            !!!cp (158);
2481          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2482          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2483          !!!next-input-character;          !!!next-input-character;
2484    
2485          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2486    
2487          redo A;          redo A;
2488        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2489            !!!cp (159);
2490          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2491          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2492          ## reconsume          ## reconsume
2493    
2494          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2495    
2496          redo A;          redo A;
2497        } else {        } else {
2498          $self->{current_token}          !!!cp (160);
2499              = {type => DOCTYPE_TOKEN,          $self->{ct}->{name} = chr $self->{nc};
2500                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
                correct => 1};  
 ## ISSUE: "Set the token's name name to the" in the spec  
2501          $self->{state} = DOCTYPE_NAME_STATE;          $self->{state} = DOCTYPE_NAME_STATE;
2502          !!!next-input-character;          !!!next-input-character;
2503          redo A;          redo A;
2504        }        }
2505      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2506  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2507        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2508            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2509          $self->{state} = AFTER_DOCTYPE_NAME_STATE;          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2510          !!!next-input-character;          !!!next-input-character;
2511          redo A;          redo A;
2512        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2513            !!!cp (162);
2514          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2515          !!!next-input-character;          !!!next-input-character;
2516    
2517          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2518    
2519          redo A;          redo A;
2520        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2521            !!!cp (163);
2522          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2523          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2524          ## reconsume          ## reconsume
2525    
2526          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2527          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2528    
2529          redo A;          redo A;
2530        } else {        } else {
2531          $self->{current_token}->{name}          !!!cp (164);
2532            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2533              .= chr ($self->{nc}); # DOCTYPE
2534          ## Stay in the state          ## Stay in the state
2535          !!!next-input-character;          !!!next-input-character;
2536          redo A;          redo A;
2537        }        }
2538      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2539        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2540            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2541          ## Stay in the state          ## Stay in the state
2542          !!!next-input-character;          !!!next-input-character;
2543          redo A;          redo A;
2544        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2545            !!!cp (166);
2546          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2547          !!!next-input-character;          !!!next-input-character;
2548    
2549          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2550    
2551          redo A;          redo A;
2552        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2553            !!!cp (167);
2554          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2555          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2556          ## reconsume          ## reconsume
2557    
2558          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2559          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2560    
2561          redo A;          redo A;
2562        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2563                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2564            $self->{state} = PUBLIC_STATE;
2565            $self->{s_kwd} = chr $self->{nc};
2566          !!!next-input-character;          !!!next-input-character;
2567          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2568              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2569            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2570            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2571                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2572          !!!next-input-character;          !!!next-input-character;
2573          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2574        } else {        } else {
2575            !!!cp (180);
2576            !!!parse-error (type => 'string after DOCTYPE name');
2577            $self->{ct}->{quirks} = 1;
2578    
2579            $self->{state} = BOGUS_DOCTYPE_STATE;
2580          !!!next-input-character;          !!!next-input-character;
2581          #          redo A;
2582        }        }
2583        } elsif ($self->{state} == PUBLIC_STATE) {
2584          ## ASCII case-insensitive
2585          if ($self->{nc} == [
2586                undef,
2587                0x0055, # U
2588                0x0042, # B
2589                0x004C, # L
2590                0x0049, # I
2591              ]->[length $self->{s_kwd}] or
2592              $self->{nc} == [
2593                undef,
2594                0x0075, # u
2595                0x0062, # b
2596                0x006C, # l
2597                0x0069, # i
2598              ]->[length $self->{s_kwd}]) {
2599            !!!cp (175);
2600            ## Stay in the state.
2601            $self->{s_kwd} .= chr $self->{nc};
2602            !!!next-input-character;
2603            redo A;
2604          } elsif ((length $self->{s_kwd}) == 5 and
2605                   ($self->{nc} == 0x0043 or # C
2606                    $self->{nc} == 0x0063)) { # c
2607            !!!cp (168);
2608            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2609            !!!next-input-character;
2610            redo A;
2611          } else {
2612            !!!cp (169);
2613            !!!parse-error (type => 'string after DOCTYPE name',
2614                            line => $self->{line_prev},
2615                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2616            $self->{ct}->{quirks} = 1;
2617    
2618        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2619        $self->{state} = BOGUS_DOCTYPE_STATE;          ## Reconsume.
2620        # next-input-character is already done          redo A;
2621        redo A;        }
2622        } elsif ($self->{state} == SYSTEM_STATE) {
2623          ## ASCII case-insensitive
2624          if ($self->{nc} == [
2625                undef,
2626                0x0059, # Y
2627                0x0053, # S
2628                0x0054, # T
2629                0x0045, # E
2630              ]->[length $self->{s_kwd}] or
2631              $self->{nc} == [
2632                undef,
2633                0x0079, # y
2634                0x0073, # s
2635                0x0074, # t
2636                0x0065, # e
2637              ]->[length $self->{s_kwd}]) {
2638            !!!cp (170);
2639            ## Stay in the state.
2640            $self->{s_kwd} .= chr $self->{nc};
2641            !!!next-input-character;
2642            redo A;
2643          } elsif ((length $self->{s_kwd}) == 5 and
2644                   ($self->{nc} == 0x004D or # M
2645                    $self->{nc} == 0x006D)) { # m
2646            !!!cp (171);
2647            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2648            !!!next-input-character;
2649            redo A;
2650          } else {
2651            !!!cp (172);
2652            !!!parse-error (type => 'string after DOCTYPE name',
2653                            line => $self->{line_prev},
2654                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2655            $self->{ct}->{quirks} = 1;
2656    
2657            $self->{state} = BOGUS_DOCTYPE_STATE;
2658            ## Reconsume.
2659            redo A;
2660          }
2661      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2662        if ({        if ($is_space->{$self->{nc}}) {
2663              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (181);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2664          ## Stay in the state          ## Stay in the state
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2668          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2669            $self->{ct}->{pubid} = ''; # DOCTYPE
2670          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2674          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2675            $self->{ct}->{pubid} = ''; # DOCTYPE
2676          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2680            !!!cp (184);
2681          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2682    
2683          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2684          !!!next-input-character;          !!!next-input-character;
2685    
2686          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2687          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2688    
2689          redo A;          redo A;
2690        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2691            !!!cp (185);
2692          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2693    
2694          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2695          ## reconsume          ## reconsume
2696    
2697          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2698          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2699    
2700          redo A;          redo A;
2701        } else {        } else {
2702            !!!cp (186);
2703          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2704            $self->{ct}->{quirks} = 1;
2705    
2706          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708          redo A;          redo A;
2709        }        }
2710      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2711        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2712            !!!cp (187);
2713          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2714          !!!next-input-character;          !!!next-input-character;
2715          redo A;          redo A;
2716        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2717            !!!cp (188);
2718          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2719    
2720          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2721          !!!next-input-character;          !!!next-input-character;
2722    
2723          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2724          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2725    
2726          redo A;          redo A;
2727        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2728            !!!cp (189);
2729          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2730    
2731          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2732          ## reconsume          ## reconsume
2733    
2734          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2735          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2736    
2737          redo A;          redo A;
2738        } else {        } else {
2739          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2740              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2741                .= chr $self->{nc};
2742            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2743                                  length $self->{ct}->{pubid});
2744    
2745          ## Stay in the state          ## Stay in the state
2746          !!!next-input-character;          !!!next-input-character;
2747          redo A;          redo A;
2748        }        }
2749      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2750        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2751            !!!cp (191);
2752          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2753          !!!next-input-character;          !!!next-input-character;
2754          redo A;          redo A;
2755        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2756            !!!cp (192);
2757          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2758    
2759          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2760          !!!next-input-character;          !!!next-input-character;
2761    
2762          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2763          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2764    
2765          redo A;          redo A;
2766        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2767            !!!cp (193);
2768          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2769    
2770          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2771          ## reconsume          ## reconsume
2772    
2773          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2774          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2775    
2776          redo A;          redo A;
2777        } else {        } else {
2778          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2779              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2780                .= chr $self->{nc};
2781            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2782                                  length $self->{ct}->{pubid});
2783    
2784          ## Stay in the state          ## Stay in the state
2785          !!!next-input-character;          !!!next-input-character;
2786          redo A;          redo A;
2787        }        }
2788      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2789        if ({        if ($is_space->{$self->{nc}}) {
2790              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2791          ## Stay in the state          ## Stay in the state
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2795          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2796            $self->{ct}->{sysid} = ''; # DOCTYPE
2797          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2801          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2802            $self->{ct}->{sysid} = ''; # DOCTYPE
2803          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2804          !!!next-input-character;          !!!next-input-character;
2805          redo A;          redo A;
2806        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2807            !!!cp (198);
2808          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2809          !!!next-input-character;          !!!next-input-character;
2810    
2811          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2812    
2813          redo A;          redo A;
2814        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2815            !!!cp (199);
2816          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2817    
2818          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2819          ## reconsume          ## reconsume
2820    
2821          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2822          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2823    
2824          redo A;          redo A;
2825        } else {        } else {
2826            !!!cp (200);
2827          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2828            $self->{ct}->{quirks} = 1;
2829    
2830          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2831          !!!next-input-character;          !!!next-input-character;
2832          redo A;          redo A;
2833        }        }
2834      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2835        if ({        if ($is_space->{$self->{nc}}) {
2836              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2837          ## Stay in the state          ## Stay in the state
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2841          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2842            $self->{ct}->{sysid} = ''; # DOCTYPE
2843          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2847          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2848            $self->{ct}->{sysid} = ''; # DOCTYPE
2849          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851          redo A;          redo A;
2852        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2853            !!!cp (204);
2854          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2855          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2856          !!!next-input-character;          !!!next-input-character;
2857    
2858          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2859          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2860    
2861          redo A;          redo A;
2862        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2863            !!!cp (205);
2864          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2865    
2866          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2867          ## reconsume          ## reconsume
2868    
2869          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2870          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2871    
2872          redo A;          redo A;
2873        } else {        } else {
2874            !!!cp (206);
2875          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2876            $self->{ct}->{quirks} = 1;
2877    
2878          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880          redo A;          redo A;
2881        }        }
2882      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2883        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2884            !!!cp (207);
2885          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2886          !!!next-input-character;          !!!next-input-character;
2887          redo A;          redo A;
2888        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2889          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!cp (208);
2890            !!!parse-error (type => 'unclosed SYSTEM literal');
2891    
2892          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2893          !!!next-input-character;          !!!next-input-character;
2894    
2895          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2896          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2897    
2898          redo A;          redo A;
2899        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2900            !!!cp (209);
2901          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2902    
2903          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2904          ## reconsume          ## reconsume
2905    
2906          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2907          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2908    
2909          redo A;          redo A;
2910        } else {        } else {
2911          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2912              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2913                .= chr $self->{nc};
2914            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2915                                  length $self->{ct}->{sysid});
2916    
2917          ## Stay in the state          ## Stay in the state
2918          !!!next-input-character;          !!!next-input-character;
2919          redo A;          redo A;
2920        }        }
2921      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2922        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2923            !!!cp (211);
2924          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2925          !!!next-input-character;          !!!next-input-character;
2926          redo A;          redo A;
2927        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2928          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!cp (212);
2929            !!!parse-error (type => 'unclosed SYSTEM literal');
2930    
2931          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2932          !!!next-input-character;          !!!next-input-character;
2933    
2934          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2935          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2936    
2937          redo A;          redo A;
2938        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2939            !!!cp (213);
2940          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2941    
2942          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2943          ## reconsume          ## reconsume
2944    
2945          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2946          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2947    
2948          redo A;          redo A;
2949        } else {        } else {
2950          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2951              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2952                .= chr $self->{nc};
2953            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2954                                  length $self->{ct}->{sysid});
2955    
2956          ## Stay in the state          ## Stay in the state
2957          !!!next-input-character;          !!!next-input-character;
2958          redo A;          redo A;
2959        }        }
2960      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2961        if ({        if ($is_space->{$self->{nc}}) {
2962              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2963          ## Stay in the state          ## Stay in the state
2964          !!!next-input-character;          !!!next-input-character;
2965          redo A;          redo A;
2966        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2967            !!!cp (216);
2968          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2969          !!!next-input-character;          !!!next-input-character;
2970    
2971          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2972    
2973          redo A;          redo A;
2974        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2975            !!!cp (217);
2976          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
   
2977          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2978          ## reconsume          ## reconsume
2979    
2980          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2981          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2982    
2983          redo A;          redo A;
2984        } else {        } else {
2985            !!!cp (218);
2986          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2987            #$self->{ct}->{quirks} = 1;
2988    
2989          $self->{state} = BOGUS_DOCTYPE_STATE;          $self->{state} = BOGUS_DOCTYPE_STATE;
2990          !!!next-input-character;          !!!next-input-character;
2991          redo A;          redo A;
2992        }        }
2993      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2994        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2995            !!!cp (219);
2996          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
2997          !!!next-input-character;          !!!next-input-character;
2998    
2999          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3000    
3001          redo A;          redo A;
3002        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
3003          !!!parse-error (type => 'unclosed DOCTYPE');          !!!cp (220);
3004          $self->{state} = DATA_STATE;          $self->{state} = DATA_STATE;
3005          ## reconsume          ## reconsume
3006    
3007          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3008    
3009          redo A;          redo A;
3010        } else {        } else {
3011            !!!cp (221);
3012            my $s = '';
3013            $self->{read_until}->($s, q[>], 0);
3014    
3015          ## Stay in the state          ## Stay in the state
3016          !!!next-input-character;          !!!next-input-character;
3017          redo A;          redo A;
3018        }        }
3019      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3020        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3021      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3022    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3023          
3024    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3025  } # _get_next_token          !!!cp (221.1);
3026            $self->{state} = CDATA_SECTION_MSE1_STATE;
3027            !!!next-input-character;
3028            redo A;
3029          } elsif ($self->{nc} == -1) {
3030            $self->{state} = DATA_STATE;
3031            !!!next-input-character;
3032            if (length $self->{ct}->{data}) { # character
3033              !!!cp (221.2);
3034              !!!emit ($self->{ct}); # character
3035            } else {
3036              !!!cp (221.3);
3037              ## No token to emit. $self->{ct} is discarded.
3038            }        
3039            redo A;
3040          } else {
3041            !!!cp (221.4);
3042            $self->{ct}->{data} .= chr $self->{nc};
3043            $self->{read_until}->($self->{ct}->{data},
3044                                  q<]>,
3045                                  length $self->{ct}->{data});
3046    
3047  sub _tokenize_attempt_to_consume_an_entity ($$$) {          ## Stay in the state.
3048    my ($self, $in_attr, $additional) = @_;          !!!next-input-character;
3049            redo A;
3050          }
3051    
3052    if ({        ## ISSUE: "text tokens" in spec.
3053         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3054         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3055         $additional => 1,          !!!cp (221.5);
3056        }->{$self->{next_input_character}}) {          $self->{state} = CDATA_SECTION_MSE2_STATE;
3057      ## Don't consume          !!!next-input-character;
3058      ## No error          redo A;
3059      return undef;        } else {
3060    } elsif ($self->{next_input_character} == 0x0023) { # #          !!!cp (221.6);
3061      !!!next-input-character;          $self->{ct}->{data} .= ']';
3062      if ($self->{next_input_character} == 0x0078 or # x          $self->{state} = CDATA_SECTION_STATE;
3063          $self->{next_input_character} == 0x0058) { # X          ## Reconsume.
3064        my $code;          redo A;
3065        X: {        }
3066          my $x_char = $self->{next_input_character};      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3067          !!!next-input-character;        if ($self->{nc} == 0x003E) { # >
3068          if (0x0030 <= $self->{next_input_character} and          $self->{state} = DATA_STATE;
3069              $self->{next_input_character} <= 0x0039) { # 0..9          !!!next-input-character;
3070            $code ||= 0;          if (length $self->{ct}->{data}) { # character
3071            $code *= 0x10;            !!!cp (221.7);
3072            $code += $self->{next_input_character} - 0x0030;            !!!emit ($self->{ct}); # character
           redo X;  
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3073          } else {          } else {
3074            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3075              ## No token to emit. $self->{ct} is discarded.
3076          }          }
3077            redo A;
3078          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3079            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3080            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3081          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
           !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!parse-error (type => 'CR character reference');  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => CHARACTER_TOKEN, data => chr $code,  
                 has_reference => 1};  
       } # X  
     } elsif (0x0030 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x0039) { # 0..9  
       my $code = $self->{next_input_character} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_input_character} and  
                 $self->{next_input_character} <= 0x0039) { # 0..9  
         $code *= 10;  
         $code += $self->{next_input_character} - 0x0030;  
           
3082          !!!next-input-character;          !!!next-input-character;
3083            redo A;
3084          } else {
3085            !!!cp (221.11);
3086            $self->{ct}->{data} .= ']]'; # character
3087            $self->{state} = CDATA_SECTION_STATE;
3088            ## Reconsume.
3089            redo A;
3090          }
3091        } elsif ($self->{state} == ENTITY_STATE) {
3092          if ($is_space->{$self->{nc}} or
3093              {
3094                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3095                $self->{entity_add} => 1,
3096              }->{$self->{nc}}) {
3097            !!!cp (1001);
3098            ## Don't consume
3099            ## No error
3100            ## Return nothing.
3101            #
3102          } elsif ($self->{nc} == 0x0023) { # #
3103            !!!cp (999);
3104            $self->{state} = ENTITY_HASH_STATE;
3105            $self->{s_kwd} = '#';
3106            !!!next-input-character;
3107            redo A;
3108          } elsif ((0x0041 <= $self->{nc} and
3109                    $self->{nc} <= 0x005A) or # A..Z
3110                   (0x0061 <= $self->{nc} and
3111                    $self->{nc} <= 0x007A)) { # a..z
3112            !!!cp (998);
3113            require Whatpm::_NamedEntityList;
3114            $self->{state} = ENTITY_NAME_STATE;
3115            $self->{s_kwd} = chr $self->{nc};
3116            $self->{entity__value} = $self->{s_kwd};
3117            $self->{entity__match} = 0;
3118            !!!next-input-character;
3119            redo A;
3120          } else {
3121            !!!cp (1027);
3122            !!!parse-error (type => 'bare ero');
3123            ## Return nothing.
3124            #
3125        }        }
3126    
3127        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3128          ## reference" algorithm.  In other word, there is an "&" character
3129          ## that does not introduce a character reference, which would be
3130          ## appended to the parent element or the attribute value in later
3131          ## process of the tokenizer.
3132    
3133          if ($self->{prev_state} == DATA_STATE) {
3134            !!!cp (997);
3135            $self->{state} = $self->{prev_state};
3136            ## Reconsume.
3137            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3138                      line => $self->{line_prev},
3139                      column => $self->{column_prev},
3140                     });
3141            redo A;
3142          } else {
3143            !!!cp (996);
3144            $self->{ca}->{value} .= '&';
3145            $self->{state} = $self->{prev_state};
3146            ## Reconsume.
3147            redo A;
3148          }
3149        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3150          if ($self->{nc} == 0x0078 or # x
3151              $self->{nc} == 0x0058) { # X
3152            !!!cp (995);
3153            $self->{state} = HEXREF_X_STATE;
3154            $self->{s_kwd} .= chr $self->{nc};
3155            !!!next-input-character;
3156            redo A;
3157          } elsif (0x0030 <= $self->{nc} and
3158                   $self->{nc} <= 0x0039) { # 0..9
3159            !!!cp (994);
3160            $self->{state} = NCR_NUM_STATE;
3161            $self->{s_kwd} = $self->{nc} - 0x0030;
3162            !!!next-input-character;
3163            redo A;
3164          } else {
3165            !!!parse-error (type => 'bare nero',
3166                            line => $self->{line_prev},
3167                            column => $self->{column_prev} - 1);
3168    
3169            ## NOTE: According to the spec algorithm, nothing is returned,
3170            ## and then "&#" is appended to the parent element or the attribute
3171            ## value in the later processing.
3172    
3173            if ($self->{prev_state} == DATA_STATE) {
3174              !!!cp (1019);
3175              $self->{state} = $self->{prev_state};
3176              ## Reconsume.
3177              !!!emit ({type => CHARACTER_TOKEN,
3178                        data => '&#',
3179                        line => $self->{line_prev},
3180                        column => $self->{column_prev} - 1,
3181                       });
3182              redo A;
3183            } else {
3184              !!!cp (993);
3185              $self->{ca}->{value} .= '&#';
3186              $self->{state} = $self->{prev_state};
3187              ## Reconsume.
3188              redo A;
3189            }
3190          }
3191        } elsif ($self->{state} == NCR_NUM_STATE) {
3192          if (0x0030 <= $self->{nc} and
3193              $self->{nc} <= 0x0039) { # 0..9
3194            !!!cp (1012);
3195            $self->{s_kwd} *= 10;
3196            $self->{s_kwd} += $self->{nc} - 0x0030;
3197            
3198            ## Stay in the state.
3199            !!!next-input-character;
3200            redo A;
3201          } elsif ($self->{nc} == 0x003B) { # ;
3202            !!!cp (1013);
3203          !!!next-input-character;          !!!next-input-character;
3204            #
3205        } else {        } else {
3206            !!!cp (1014);
3207          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3208            ## Reconsume.
3209            #
3210        }        }
3211    
3212        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3213          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3214          my $c = $self->{column_prev};
3215          if ($charref_map->{$code}) {
3216            !!!cp (1015);
3217            !!!parse-error (type => 'invalid character reference',
3218                            text => (sprintf 'U+%04X', $code),
3219                            line => $l, column => $c);
3220            $code = $charref_map->{$code};
3221          } elsif ($code > 0x10FFFF) {
3222            !!!cp (1016);
3223            !!!parse-error (type => 'invalid character reference',
3224                            text => (sprintf 'U-%08X', $code),
3225                            line => $l, column => $c);
3226          $code = 0xFFFD;          $code = 0xFFFD;
3227          }
3228    
3229          if ($self->{prev_state} == DATA_STATE) {
3230            !!!cp (992);
3231            $self->{state} = $self->{prev_state};
3232            ## Reconsume.
3233            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3234                      line => $l, column => $c,
3235                     });
3236            redo A;
3237          } else {
3238            !!!cp (991);
3239            $self->{ca}->{value} .= chr $code;
3240            $self->{ca}->{has_reference} = 1;
3241            $self->{state} = $self->{prev_state};
3242            ## Reconsume.
3243            redo A;
3244          }
3245        } elsif ($self->{state} == HEXREF_X_STATE) {
3246          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3247              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3248              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3249            # 0..9, A..F, a..f
3250            !!!cp (990);
3251            $self->{state} = HEXREF_HEX_STATE;
3252            $self->{s_kwd} = 0;
3253            ## Reconsume.
3254            redo A;
3255          } else {
3256            !!!parse-error (type => 'bare hcro',
3257                            line => $self->{line_prev},
3258                            column => $self->{column_prev} - 2);
3259    
3260            ## NOTE: According to the spec algorithm, nothing is returned,
3261            ## and then "&#" followed by "X" or "x" is appended to the parent
3262            ## element or the attribute value in the later processing.
3263    
3264            if ($self->{prev_state} == DATA_STATE) {
3265              !!!cp (1005);
3266              $self->{state} = $self->{prev_state};
3267              ## Reconsume.
3268              !!!emit ({type => CHARACTER_TOKEN,
3269                        data => '&' . $self->{s_kwd},
3270                        line => $self->{line_prev},
3271                        column => $self->{column_prev} - length $self->{s_kwd},
3272                       });
3273              redo A;
3274            } else {
3275              !!!cp (989);
3276              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3277              $self->{state} = $self->{prev_state};
3278              ## Reconsume.
3279              redo A;
3280            }
3281          }
3282        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3283          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3284            # 0..9
3285            !!!cp (1002);
3286            $self->{s_kwd} *= 0x10;
3287            $self->{s_kwd} += $self->{nc} - 0x0030;
3288            ## Stay in the state.
3289            !!!next-input-character;
3290            redo A;
3291          } elsif (0x0061 <= $self->{nc} and
3292                   $self->{nc} <= 0x0066) { # a..f
3293            !!!cp (1003);
3294            $self->{s_kwd} *= 0x10;
3295            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3296            ## Stay in the state.
3297            !!!next-input-character;
3298            redo A;
3299          } elsif (0x0041 <= $self->{nc} and
3300                   $self->{nc} <= 0x0046) { # A..F
3301            !!!cp (1004);
3302            $self->{s_kwd} *= 0x10;
3303            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3304            ## Stay in the state.
3305            !!!next-input-character;
3306            redo A;
3307          } elsif ($self->{nc} == 0x003B) { # ;
3308            !!!cp (1006);
3309            !!!next-input-character;
3310            #
3311          } else {
3312            !!!cp (1007);
3313            !!!parse-error (type => 'no refc',
3314                            line => $self->{line},
3315                            column => $self->{column});
3316            ## Reconsume.
3317            #
3318          }
3319    
3320          my $code = $self->{s_kwd};
3321          my $l = $self->{line_prev};
3322          my $c = $self->{column_prev};
3323          if ($charref_map->{$code}) {
3324            !!!cp (1008);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U+%04X', $code),
3327                            line => $l, column => $c);
3328            $code = $charref_map->{$code};
3329        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3330          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3331            !!!parse-error (type => 'invalid character reference',
3332                            text => (sprintf 'U-%08X', $code),
3333                            line => $l, column => $c);
3334          $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};  
3335        }        }
3336          
3337        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1};        if ($self->{prev_state} == DATA_STATE) {
3338      } else {          !!!cp (988);
3339        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3340        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3341        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3342        return undef;                    line => $l, column => $c,
3343      }                   });
3344    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3345              $self->{next_input_character} <= 0x005A) or        } else {
3346             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3347              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3348      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3349      !!!next-input-character;          $self->{state} = $self->{prev_state};
3350            ## Reconsume.
3351      my $value = $entity_name;          redo A;
3352      my $match = 0;        }
3353      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3354      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3355              ## NOTE: Some number greater than the maximum length of entity name
3356      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3357             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3358             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3359               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3360              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3361               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3362              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3363               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3364              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3365        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3366        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3367          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3368            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369            $match = 1;              $self->{entity__match} = 1;
3370            !!!next-input-character;              !!!next-input-character;
3371            last;              #
3372              } else {
3373                !!!cp (1021);
3374                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3375                $self->{entity__match} = -1;
3376                ## Stay in the state.
3377                !!!next-input-character;
3378                redo A;
3379              }
3380          } else {          } else {
3381            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3382            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3383              $self->{entity__match} *= 2;
3384              ## Stay in the state.
3385            !!!next-input-character;            !!!next-input-character;
3386              redo A;
3387          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3388        }        }
3389      }  
3390              my $data;
3391      if ($match > 0) {        my $has_ref;
3392        return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};        if ($self->{entity__match} > 0) {
3393      } elsif ($match < 0) {          !!!cp (1023);
3394        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3395        if ($in_attr and $match < -1) {          $has_ref = 1;
3396          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          #
3397          } elsif ($self->{entity__match} < 0) {
3398            !!!parse-error (type => 'no refc');
3399            if ($self->{prev_state} != DATA_STATE and # in attribute
3400                $self->{entity__match} < -1) {
3401              !!!cp (1024);
3402              $data = '&' . $self->{s_kwd};
3403              #
3404            } else {
3405              !!!cp (1025);
3406              $data = $self->{entity__value};
3407              $has_ref = 1;
3408              #
3409            }
3410        } else {        } else {
3411          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1};          !!!cp (1026);
3412            !!!parse-error (type => 'bare ero',
3413                            line => $self->{line_prev},
3414                            column => $self->{column_prev} - length $self->{s_kwd});
3415            $data = '&' . $self->{s_kwd};
3416            #
3417          }
3418      
3419          ## NOTE: In these cases, when a character reference is found,
3420          ## it is consumed and a character token is returned, or, otherwise,
3421          ## nothing is consumed and returned, according to the spec algorithm.
3422          ## In this implementation, anything that has been examined by the
3423          ## tokenizer is appended to the parent element or the attribute value
3424          ## as string, either literal string when no character reference or
3425          ## entity-replaced string otherwise, in this stage, since any characters
3426          ## that would not be consumed are appended in the data state or in an
3427          ## appropriate attribute value state anyway.
3428    
3429          if ($self->{prev_state} == DATA_STATE) {
3430            !!!cp (986);
3431            $self->{state} = $self->{prev_state};
3432            ## Reconsume.
3433            !!!emit ({type => CHARACTER_TOKEN,
3434                      data => $data,
3435                      line => $self->{line_prev},
3436                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3437                     });
3438            redo A;
3439          } else {
3440            !!!cp (985);
3441            $self->{ca}->{value} .= $data;
3442            $self->{ca}->{has_reference} = 1 if $has_ref;
3443            $self->{state} = $self->{prev_state};
3444            ## Reconsume.
3445            redo A;
3446        }        }
3447      } else {      } else {
3448        !!!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};  
3449      }      }
3450    } else {    } # A  
3451      ## no characters are consumed  
3452      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3453      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3454    
3455  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3456    my $self = shift;    my $self = shift;
# Line 2080  sub _initialize_tree_constructor ($) { Line 3459  sub _initialize_tree_constructor ($) {
3459    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3460    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3461    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3462      $self->{document}->set_user_data (manakai_source_line => 1);
3463      $self->{document}->set_user_data (manakai_source_column => 1);
3464  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3465    
3466  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 2099  sub _construct_tree ($) { Line 3480  sub _construct_tree ($) {
3480    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3481    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3482    ## 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  
3483        
3484    !!!next-token;    !!!next-token;
3485    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
3486    undef $self->{form_element};    undef $self->{form_element};
3487    undef $self->{head_element};    undef $self->{head_element};
3488      undef $self->{head_element_inserted};
3489    $self->{open_elements} = [];    $self->{open_elements} = [];
3490    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3491      undef $self->{ignore_newline};
3492    
3493      ## NOTE: The "initial" insertion mode.
3494    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3495    
3496      ## NOTE: The "before html" insertion mode.
3497    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3498      $self->{insertion_mode} = BEFORE_HEAD_IM;
3499    
3500      ## NOTE: The "before head" insertion mode and so on.
3501    $self->_tree_construction_main;    $self->_tree_construction_main;
3502  } # _construct_tree  } # _construct_tree
3503    
3504  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3505    my $self = shift;    my $self = shift;
3506    
3507      ## NOTE: "initial" insertion mode
3508    
3509    INITIAL: {    INITIAL: {
3510      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3511        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 2126  sub _tree_construction_initial ($) { Line 3513  sub _tree_construction_initial ($) {
3513        ## language.        ## language.
3514        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3515        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3516        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3517        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3518            defined $token->{public_identifier} or            defined $token->{sysid}) {
3519            defined $token->{system_identifier}) {          !!!cp ('t1');
3520          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3521        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3522          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3523          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3524          } elsif (defined $token->{pubid}) {
3525            if ($token->{pubid} eq 'XSLT-compat') {
3526              !!!cp ('t1.2');
3527              !!!parse-error (type => 'XSLT-compat', token => $token,
3528                              level => $self->{level}->{should});
3529            } else {
3530              !!!parse-error (type => 'not HTML5', token => $token);
3531            }
3532          } else {
3533            !!!cp ('t3');
3534            #
3535        }        }
3536                
3537        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3538          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3539        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3540            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3541        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3542            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3543        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3544        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3545        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3546                
3547        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3548            !!!cp ('t4');
3549          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3550        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3551          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3552          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3553          if ({          my $prefix = [
3554            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3555            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3556            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3557            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3558            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3559            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3560            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3561            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3562            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3563            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3564            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3565            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3566            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3567            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3568            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3569            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3570            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3571            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3572            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3573            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3574            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3575            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3576            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3577            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3578            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3579            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3580            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3581            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3582            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3583            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3584            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3585            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3586            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3587            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3588            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3589            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3590            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3591            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3592            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3593            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3594            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3595            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3596            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3597            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3598            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3599            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3600            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3601            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3602            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3603            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3604            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3605            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD W3 HTML//",
3606            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3607            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3608            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3609            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,          ]; # $prefix
3610            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,          my $match;
3611            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,          for (@$prefix) {
3612            "-//W3C//DTD HTML 3.2//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3613            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,              $match = 1;
3614            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,              last;
3615            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            }
3616            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,          }
3617            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,          if ($match or
3618            "-//W3C//DTD W3 HTML//EN" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3619            "-//W3O//DTD W3 HTML 3.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3620            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,              $pubid eq "HTML") {
3621            "-//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}) {  
3622            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3623          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3624                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3625            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3626                !!!cp ('t6');
3627              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3628            } else {            } else {
3629                !!!cp ('t7');
3630              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3631            }            }
3632          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3633                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3634              !!!cp ('t8');
3635            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3636            } else {
3637              !!!cp ('t9');
3638          }          }
3639          } else {
3640            !!!cp ('t10');
3641        }        }
3642        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3643          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3644          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3645          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") {
3646              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3647              ## marked as quirks.
3648            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3649              !!!cp ('t11');
3650            } else {
3651              !!!cp ('t12');
3652          }          }
3653          } else {
3654            !!!cp ('t13');
3655        }        }
3656                
3657        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3658        !!!next-token;        !!!next-token;
3659        return;        return;
3660      } elsif ({      } elsif ({
# Line 2254  sub _tree_construction_initial ($) { Line 3662  sub _tree_construction_initial ($) {
3662                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
3663                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3664               }->{$token->{type}}) {               }->{$token->{type}}) {
3665        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3666          !!!parse-error (type => 'no DOCTYPE', token => $token);
3667        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3668        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3669        ## reprocess        ## reprocess
3670          !!!ack-later;
3671        return;        return;
3672      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3673        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3674          ## Ignore the token          ## Ignore the token
3675    
3676          unless (length $token->{data}) {          unless (length $token->{data}) {
3677            ## Stay in the phase            !!!cp ('t15');
3678              ## Stay in the insertion mode.
3679            !!!next-token;            !!!next-token;
3680            redo INITIAL;            redo INITIAL;
3681            } else {
3682              !!!cp ('t16');
3683          }          }
3684          } else {
3685            !!!cp ('t17');
3686        }        }
3687    
3688        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3689        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3690        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3691        ## reprocess        ## reprocess
3692        return;        return;
3693      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3694          !!!cp ('t18');
3695        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3696        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3697                
3698        ## Stay in the phase.        ## Stay in the insertion mode.
3699        !!!next-token;        !!!next-token;
3700        redo INITIAL;        redo INITIAL;
3701      } else {      } else {
3702        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
3703      }      }
3704    } # INITIAL    } # INITIAL
3705    
3706      die "$0: _tree_construction_initial: This should be never reached";
3707  } # _tree_construction_initial  } # _tree_construction_initial
3708    
3709  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3710    my $self = shift;    my $self = shift;
3711    
3712      ## NOTE: "before html" insertion mode.
3713        
3714    B: {    B: {
3715        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3716          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3717            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3718          ## Ignore the token          ## Ignore the token
3719          ## Stay in the phase          ## Stay in the insertion mode.
3720          !!!next-token;          !!!next-token;
3721          redo B;          redo B;
3722        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
3723            !!!cp ('t20');
3724          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3725          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3726          ## Stay in the phase          ## Stay in the insertion mode.
3727          !!!next-token;          !!!next-token;
3728          redo B;          redo B;
3729        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3730          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3731            ## Ignore the token.            ## Ignore the token.
3732    
3733            unless (length $token->{data}) {            unless (length $token->{data}) {
3734              ## Stay in the phase              !!!cp ('t21');
3735                ## Stay in the insertion mode.
3736              !!!next-token;              !!!next-token;
3737              redo B;              redo B;
3738              } else {
3739                !!!cp ('t22');
3740            }            }
3741            } else {
3742              !!!cp ('t23');
3743          }          }
3744    
3745          $self->{application_cache_selection}->(undef);          $self->{application_cache_selection}->(undef);
3746    
3747          #          #
3748        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3749          if ($token->{tag_name} eq 'html' and          if ($token->{tag_name} eq 'html') {
3750              $token->{attributes}->{manifest}) {            my $root_element;
3751            $self->{application_cache_selection}            !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3752                 ->($token->{attributes}->{manifest}->{value});            $self->{document}->append_child ($root_element);
3753            ## ISSUE: No relative reference resolution?            push @{$self->{open_elements}},
3754                  [$root_element, $el_category->{html}];
3755    
3756              if ($token->{attributes}->{manifest}) {
3757                !!!cp ('t24');
3758                $self->{application_cache_selection}
3759                    ->($token->{attributes}->{manifest}->{value});
3760                ## ISSUE: Spec is unclear on relative references.
3761                ## According to Hixie (#whatwg 2008-03-19), it should be
3762                ## resolved against the base URI of the document in HTML
3763                ## or xml:base of the element in XHTML.
3764              } else {
3765                !!!cp ('t25');
3766                $self->{application_cache_selection}->(undef);
3767              }
3768    
3769              !!!nack ('t25c');
3770    
3771              !!!next-token;
3772              return; ## Go to the "before head" insertion mode.
3773          } else {          } else {
3774            $self->{application_cache_selection}->(undef);            !!!cp ('t25.1');
3775              #
3776          }          }
   
         ## ISSUE: There is an issue in the spec  
         #  
3777        } elsif ({        } elsif ({
3778                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
3779                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
3780                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3781          $self->{application_cache_selection}->(undef);          !!!cp ('t26');
   
         ## ISSUE: There is an issue in the spec  
3782          #          #
3783        } else {        } else {
3784          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3785        }        }
3786    
3787        my $root_element; !!!create-element ($root_element, 'html');      my $root_element;
3788        $self->{document}->append_child ($root_element);      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3789        push @{$self->{open_elements}}, [$root_element, 'html'];      $self->{document}->append_child ($root_element);
3790        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3791        #redo B;  
3792        return; ## Go to the main phase.      $self->{application_cache_selection}->(undef);
3793    
3794        ## NOTE: Reprocess the token.
3795        !!!ack-later;
3796        return; ## Go to the "before head" insertion mode.
3797    } # B    } # B
3798    
3799      die "$0: _tree_construction_root_element: This should never be reached";
3800  } # _tree_construction_root_element  } # _tree_construction_root_element
3801    
3802  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2363  sub _reset_insertion_mode ($) { Line 3811  sub _reset_insertion_mode ($) {
3811            
3812      ## Step 3      ## Step 3
3813      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"!?  
3814        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3815          $last = 1;          $last = 1;
3816          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3817            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3818                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3819              #          } else {
3820            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3821          }          }
3822        }        }
3823              
3824        ## Step 4..13        ## Step 4..14
3825        my $new_mode = {        my $new_mode;
3826          if ($node->[1] & FOREIGN_EL) {
3827            !!!cp ('t28.1');
3828            ## NOTE: Strictly spaking, the line below only applies to MathML and
3829            ## SVG elements.  Currently the HTML syntax supports only MathML and
3830            ## SVG elements as foreigners.
3831            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3832          } elsif ($node->[1] == TABLE_CELL_EL) {
3833            if ($last) {
3834              !!!cp ('t28.2');
3835              #
3836            } else {
3837              !!!cp ('t28.3');
3838              $new_mode = IN_CELL_IM;
3839            }
3840          } else {
3841            !!!cp ('t28.4');
3842            $new_mode = {
3843                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3844                        td => IN_CELL_IM,                        ## NOTE: |option| and |optgroup| do not set
3845                        th => IN_CELL_IM,                        ## insertion mode to "in select" by themselves.
3846                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3847                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3848                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2395  sub _reset_insertion_mode ($) { Line 3853  sub _reset_insertion_mode ($) {
3853                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3854                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3855                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3856                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3857          }
3858        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3859                
3860        ## Step 14        ## Step 15
3861        if ($node->[1] eq 'html') {        if ($node->[1] == HTML_EL) {
3862          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3863              !!!cp ('t29');
3864            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3865          } else {          } else {
3866              ## ISSUE: Can this state be reached?
3867              !!!cp ('t30');
3868            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3869          }          }
3870          return;          return;
3871          } else {
3872            !!!cp ('t31');
3873        }        }
3874                
3875        ## Step 15        ## Step 16
3876        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3877                
3878        ## Step 16        ## Step 17
3879        $i--;        $i--;
3880        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3881                
3882        ## Step 17        ## Step 18
3883        redo S3;        redo S3;
3884      } # S3      } # S3
3885    
3886      die "$0: _reset_insertion_mode: This line should never be reached";
3887  } # _reset_insertion_mode  } # _reset_insertion_mode
3888    
3889  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2439  sub _tree_construction_main ($) { Line 3905  sub _tree_construction_main ($) {
3905      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3906      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3907        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3908            !!!cp ('t32');
3909          return;          return;
3910        }        }
3911      }      }
# Line 2453  sub _tree_construction_main ($) { Line 3920  sub _tree_construction_main ($) {
3920    
3921        ## Step 6        ## Step 6
3922        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3923            !!!cp ('t33_1');
3924          #          #
3925        } else {        } else {
3926          my $in_open_elements;          my $in_open_elements;
3927          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3928            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3929                !!!cp ('t33');
3930              $in_open_elements = 1;              $in_open_elements = 1;
3931              last OE;              last OE;
3932            }            }
3933          }          }
3934          if ($in_open_elements) {          if ($in_open_elements) {
3935              !!!cp ('t34');
3936            #            #
3937          } else {          } else {
3938              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3939              !!!cp ('t35');
3940            redo S4;            redo S4;
3941          }          }
3942        }        }
# Line 2487  sub _tree_construction_main ($) { Line 3959  sub _tree_construction_main ($) {
3959    
3960        ## Step 11        ## Step 11
3961        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3962            !!!cp ('t36');
3963          ## Step 7'          ## Step 7'
3964          $i++;          $i++;
3965          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3966                    
3967          redo S7;          redo S7;
3968        }        }
3969    
3970          !!!cp ('t37');
3971      } # S7      } # S7
3972    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3973    
3974    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3975      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3976        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3977            !!!cp ('t38');
3978          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3979          return;          return;
3980        }        }
3981      }      }
3982    
3983        !!!cp ('t39');
3984    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3985    
3986    my $parse_rcdata = sub ($$) {    my $insert;
3987      my ($content_model_flag, $insert) = @_;  
3988      my $parse_rcdata = sub ($) {
3989        my ($content_model_flag) = @_;
3990    
3991      ## Step 1      ## Step 1
3992      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3993      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3994    
3995      ## Step 2      ## Step 2
     $insert->($el); # /context node/->append_child ($el)  
   
     ## Step 3  
3996      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3997      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3998    
3999      ## Step 4      ## Step 3, 4
4000      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);  
     }  
4001    
4002      ## Step 6      !!!nack ('t40.1');
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     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";  
     }  
4003      !!!next-token;      !!!next-token;
4004    }; # $parse_rcdata    }; # $parse_rcdata
4005    
4006    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
4007      my $insert = $_[0];      ## Step 1
4008      my $script_el;      my $script_el;
4009      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4010    
4011        ## Step 2
4012      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4013    
4014        ## Step 3
4015        ## TODO: Mark as "already executed", if ...
4016    
4017        ## Step 4
4018        $insert->($script_el);
4019    
4020        ## ISSUE: $script_el is not put into the stack
4021        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4022    
4023        ## Step 5
4024      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4025      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;  
4026    
4027      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4028          $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  
4029    
4030        $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...  
     }  
       
4031      !!!next-token;      !!!next-token;
4032    }; # $script_start_tag    }; # $script_start_tag
4033    
4034      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4035      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4036      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4037      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4038    
4039    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4040      my $tag_name = shift;      my $end_tag_token = shift;
4041        my $tag_name = $end_tag_token->{tag_name};
4042    
4043        ## NOTE: The adoption agency algorithm (AAA).
4044    
4045      FET: {      FET: {
4046        ## Step 1        ## Step 1
4047        my $formatting_element;        my $formatting_element;
4048        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4049        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4050          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4051              !!!cp ('t52');
4052              last AFE;
4053            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4054                         eq $tag_name) {
4055              !!!cp ('t51');
4056            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4057            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4058            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4059          }          }
4060        } # AFE        } # AFE
4061        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4062          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4063            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4064          ## Ignore the token          ## Ignore the token
4065          !!!next-token;          !!!next-token;
4066          return;          return;
# Line 2625  sub _tree_construction_main ($) { Line 4072  sub _tree_construction_main ($) {
4072          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4073          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4074            if ($in_scope) {            if ($in_scope) {
4075                !!!cp ('t54');
4076              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4077              last INSCOPE;              last INSCOPE;
4078            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4079              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4080                !!!parse-error (type => 'unmatched end tag',
4081                                text => $token->{tag_name},
4082                                token => $end_tag_token);
4083              ## Ignore the token              ## Ignore the token
4084              !!!next-token;              !!!next-token;
4085              return;              return;
4086            }            }
4087          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4088                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4089            $in_scope = 0;            $in_scope = 0;
4090          }          }
4091        } # INSCOPE        } # INSCOPE
4092        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4093          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4094            !!!parse-error (type => 'unmatched end tag',
4095                            text => $token->{tag_name},
4096                            token => $end_tag_token);
4097          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4098          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4099          return;          return;
4100        }        }
4101        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4102          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4103            !!!parse-error (type => 'not closed',
4104                            text => $self->{open_elements}->[-1]->[0]
4105                                ->manakai_local_name,
4106                            token => $end_tag_token);
4107        }        }
4108                
4109        ## Step 2        ## Step 2
# Line 2655  sub _tree_construction_main ($) { Line 4111  sub _tree_construction_main ($) {
4111        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4112        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4113          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4114          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4115              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4116              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4117               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4118              !!!cp ('t59');
4119            $furthest_block = $node;            $furthest_block = $node;
4120            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4121              ## NOTE: The topmost (eldest) node.
4122          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4123              !!!cp ('t60');
4124            last OE;            last OE;
4125          }          }
4126        } # OE        } # OE
4127                
4128        ## Step 3        ## Step 3
4129        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4130            !!!cp ('t61');
4131          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4132          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4133          !!!next-token;          !!!next-token;
# Line 2680  sub _tree_construction_main ($) { Line 4140  sub _tree_construction_main ($) {
4140        ## Step 5        ## Step 5
4141        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4142        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4143            !!!cp ('t62');
4144          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4145        }        }
4146                
# Line 2702  sub _tree_construction_main ($) { Line 4163  sub _tree_construction_main ($) {
4163          S7S2: {          S7S2: {
4164            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4165              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4166                  !!!cp ('t63');
4167                $node_i_in_active = $_;                $node_i_in_active = $_;
4168                last S7S2;                last S7S2;
4169              }              }
# Line 2715  sub _tree_construction_main ($) { Line 4177  sub _tree_construction_main ($) {
4177                    
4178          ## Step 4          ## Step 4
4179          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4180              !!!cp ('t64');
4181            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4182          }          }
4183                    
4184          ## Step 5          ## Step 5
4185          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4186              !!!cp ('t65');
4187            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4188            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4189            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2737  sub _tree_construction_main ($) { Line 4201  sub _tree_construction_main ($) {
4201        } # S7          } # S7  
4202                
4203        ## Step 8        ## Step 8
4204        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4205            my $foster_parent_element;
4206            my $next_sibling;
4207            OE: for (reverse 0..$#{$self->{open_elements}}) {
4208              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4209                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4210                                 if (defined $parent and $parent->node_type == 1) {
4211                                   !!!cp ('t65.1');
4212                                   $foster_parent_element = $parent;
4213                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4214                                 } else {
4215                                   !!!cp ('t65.2');
4216                                   $foster_parent_element
4217                                     = $self->{open_elements}->[$_ - 1]->[0];
4218                                 }
4219                                 last OE;
4220                               }
4221                             } # OE
4222                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4223                               unless defined $foster_parent_element;
4224            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4225            $open_tables->[-1]->[1] = 1; # tainted
4226          } else {
4227            !!!cp ('t65.3');
4228            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4229          }
4230                
4231        ## Step 9        ## Step 9
4232        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2754  sub _tree_construction_main ($) { Line 4243  sub _tree_construction_main ($) {
4243        my $i;        my $i;
4244        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4245          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4246              !!!cp ('t66');
4247            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4248            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4249          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4250              !!!cp ('t67');
4251            $i = $_;            $i = $_;
4252          }          }
4253        } # AFE        } # AFE
# Line 2766  sub _tree_construction_main ($) { Line 4257  sub _tree_construction_main ($) {
4257        undef $i;        undef $i;
4258        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4259          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4260              !!!cp ('t68');
4261            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4262            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4263          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4264              !!!cp ('t69');
4265            $i = $_;            $i = $_;
4266          }          }
4267        } # OE        } # OE
4268        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4269                
4270        ## Step 14        ## Step 14
4271        redo FET;        redo FET;
4272      } # FET      } # FET
4273    }; # $formatting_end_tag    }; # $formatting_end_tag
4274    
4275    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4276      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4277    }; # $insert_to_current    }; # $insert_to_current
4278    
4279    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4280                         my $child = shift;      my $child = shift;
4281                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4282                              table => 1, tbody => 1, tfoot => 1,        # MUST
4283                              thead => 1, tr => 1,        my $foster_parent_element;
4284                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4285                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4286                           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') {  
4287                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4288                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4289                                   !!!cp ('t70');
4290                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4291                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4292                               } else {                               } else {
4293                                   !!!cp ('t71');
4294                                 $foster_parent_element                                 $foster_parent_element
4295                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4296                               }                               }
# Line 2809  sub _tree_construction_main ($) { Line 4301  sub _tree_construction_main ($) {
4301                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4302                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4303                             ($child, $next_sibling);                             ($child, $next_sibling);
4304                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4305                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4306                         }        !!!cp ('t72');
4307          $self->{open_elements}->[-1]->[0]->append_child ($child);
4308        }
4309    }; # $insert_to_foster    }; # $insert_to_foster
4310    
4311    my $insert;    ## NOTE: Insert a character (MUST): When a character is inserted, if
4312      ## the last node that was inserted by the parser is a Text node and
4313      ## the character has to be inserted after that node, then the
4314      ## character is appended to the Text node.  However, if any other
4315      ## node is inserted by the parser, then a new Text node is created
4316      ## and the character is appended as that Text node.  If I'm not
4317      ## wrong, for a parser with scripting disabled, there are only two
4318      ## cases where this occurs.  One is the case where an element node
4319      ## is inserted to the |head| element.  This is covered by using the
4320      ## |$self->{head_element_inserted}| flag.  Another is the case where
4321      ## an element or comment is inserted into the |table| subtree while
4322      ## foster parenting happens.  This is covered by using the [2] flag
4323      ## of the |$open_tables| structure.  All other cases are handled
4324      ## simply by calling |manakai_append_text| method.
4325    
4326      ## TODO: |<body><script>document.write("a<br>");
4327      ## document.body.removeChild (document.body.lastChild);
4328      ## document.write ("b")</script>|
4329    
4330    B: {    B: while (1) {
4331      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4332        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
4333          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4334        ## Ignore the token        ## Ignore the token
4335        ## Stay in the phase        ## Stay in the phase
4336        !!!next-token;        !!!next-token;
4337        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;  
4338      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4339               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4340        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4341          ## Turn into the main phase          !!!cp ('t79');
4342          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4343          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4344        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4345          ## Turn into the main phase          !!!cp ('t80');
4346          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4347          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4348          } else {
4349            !!!cp ('t81');
4350        }        }
4351    
4352  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
4353  ## 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');  
       }  
4354        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4355        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4356          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4357              !!!cp ('t84');
4358            $top_el->set_attribute_ns            $top_el->set_attribute_ns
4359              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
4360               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4361          }          }
4362        }        }
4363          !!!nack ('t84.1');
4364        !!!next-token;        !!!next-token;
4365        redo B;        next B;
4366      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4367        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4368        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4369            !!!cp ('t85');
4370          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
4371        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4372            !!!cp ('t86');
4373          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
4374        } else {        } else {
4375            !!!cp ('t87');
4376          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4377            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4378        }        }
4379        !!!next-token;        !!!next-token;
4380        redo B;        next B;
4381      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4382        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4383          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4384            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          delete $self->{ignore_newline};
4385    
4386            if (length $token->{data}) {
4387              !!!cp ('t43');
4388              $self->{open_elements}->[-1]->[0]->manakai_append_text
4389                  ($token->{data});
4390            } else {
4391              !!!cp ('t43.1');
4392            }
4393            !!!next-token;
4394            next B;
4395          } elsif ($token->{type} == END_TAG_TOKEN) {
4396            delete $self->{ignore_newline};
4397    
4398            if ($token->{tag_name} eq 'script') {
4399              !!!cp ('t50');
4400              
4401              ## Para 1-2
4402              my $script = pop @{$self->{open_elements}};
4403              
4404              ## Para 3
4405              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4406    
4407              ## Para 4
4408              ## TODO: $old_insertion_point = $current_insertion_point;
4409              ## TODO: $current_insertion_point = just before $self->{nc};
4410    
4411              ## Para 5
4412              ## TODO: Run the $script->[0].
4413    
4414              ## Para 6
4415              ## TODO: $current_insertion_point = $old_insertion_point;
4416    
4417              ## Para 7
4418              ## TODO: if ($pending_external_script) {
4419                ## TODO: ...
4420              ## TODO: }
4421    
4422              !!!next-token;
4423              next B;
4424            } else {
4425              !!!cp ('t42');
4426    
4427              pop @{$self->{open_elements}};
4428    
4429              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4430              !!!next-token;
4431              next B;
4432            }
4433          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4434            delete $self->{ignore_newline};
4435    
4436            !!!cp ('t44');
4437            !!!parse-error (type => 'not closed',
4438                            text => $self->{open_elements}->[-1]->[0]
4439                                ->manakai_local_name,
4440                            token => $token);
4441    
4442            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4443            #  ## TODO: Mark as "already executed"
4444            #}
4445    
4446            pop @{$self->{open_elements}};
4447    
4448            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4449            ## Reprocess.
4450            next B;
4451          } else {
4452            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4453          }
4454        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4455          if ($token->{type} == CHARACTER_TOKEN) {
4456            !!!cp ('t87.1');
4457            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4458            !!!next-token;
4459            next B;
4460          } elsif ($token->{type} == START_TAG_TOKEN) {
4461            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4462                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4463                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4464                ($token->{tag_name} eq 'svg' and
4465                 $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4466              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4467              !!!cp ('t87.2');
4468              #
4469            } elsif ({
4470                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4471                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4472                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4473                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4474                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4475                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4476                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4477                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4478                     }->{$token->{tag_name}}) {
4479              !!!cp ('t87.2');
4480              !!!parse-error (type => 'not closed',
4481                              text => $self->{open_elements}->[-1]->[0]
4482                                  ->manakai_local_name,
4483                              token => $token);
4484    
4485              pop @{$self->{open_elements}}
4486                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4487    
4488              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4489              ## Reprocess.
4490              next B;
4491            } else {
4492              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4493              my $tag_name = $token->{tag_name};
4494              if ($nsuri eq $SVG_NS) {
4495                $tag_name = {
4496                   altglyph => 'altGlyph',
4497                   altglyphdef => 'altGlyphDef',
4498                   altglyphitem => 'altGlyphItem',
4499                   animatecolor => 'animateColor',
4500                   animatemotion => 'animateMotion',
4501                   animatetransform => 'animateTransform',
4502                   clippath => 'clipPath',
4503                   feblend => 'feBlend',
4504                   fecolormatrix => 'feColorMatrix',
4505                   fecomponenttransfer => 'feComponentTransfer',
4506                   fecomposite => 'feComposite',
4507                   feconvolvematrix => 'feConvolveMatrix',
4508                   fediffuselighting => 'feDiffuseLighting',
4509                   fedisplacementmap => 'feDisplacementMap',
4510                   fedistantlight => 'feDistantLight',
4511                   feflood => 'feFlood',
4512                   fefunca => 'feFuncA',
4513                   fefuncb => 'feFuncB',
4514                   fefuncg => 'feFuncG',
4515                   fefuncr => 'feFuncR',
4516                   fegaussianblur => 'feGaussianBlur',
4517                   feimage => 'feImage',
4518                   femerge => 'feMerge',
4519                   femergenode => 'feMergeNode',
4520                   femorphology => 'feMorphology',
4521                   feoffset => 'feOffset',
4522                   fepointlight => 'fePointLight',
4523                   fespecularlighting => 'feSpecularLighting',
4524                   fespotlight => 'feSpotLight',
4525                   fetile => 'feTile',
4526                   feturbulence => 'feTurbulence',
4527                   foreignobject => 'foreignObject',
4528                   glyphref => 'glyphRef',
4529                   lineargradient => 'linearGradient',
4530                   radialgradient => 'radialGradient',
4531                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4532                   textpath => 'textPath',  
4533                }->{$tag_name} || $tag_name;
4534              }
4535    
4536              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4537    
4538              ## "adjust foreign attributes" - done in insert-element-f
4539    
4540              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4541    
4542              if ($self->{self_closing}) {
4543                pop @{$self->{open_elements}};
4544                !!!ack ('t87.3');
4545              } else {
4546                !!!cp ('t87.4');
4547              }
4548    
4549              !!!next-token;
4550              next B;
4551            }
4552          } elsif ($token->{type} == END_TAG_TOKEN) {
4553            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4554            !!!cp ('t87.5');
4555            #
4556          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4557            !!!cp ('t87.6');
4558            !!!parse-error (type => 'not closed',
4559                            text => $self->{open_elements}->[-1]->[0]
4560                                ->manakai_local_name,
4561                            token => $token);
4562    
4563            pop @{$self->{open_elements}}
4564                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4565    
4566            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4567    
4568            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4569            ## Reprocess.
4570            next B;
4571          } else {
4572            die "$0: $token->{type}: Unknown token type";        
4573          }
4574        }
4575    
4576        if ($self->{insertion_mode} & HEAD_IMS) {
4577          if ($token->{type} == CHARACTER_TOKEN) {
4578            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4579              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4580                if ($self->{head_element_inserted}) {
4581                  !!!cp ('t88.3');
4582                  $self->{open_elements}->[-1]->[0]->append_child
4583                    ($self->{document}->create_text_node ($1));
4584                  delete $self->{head_element_inserted};
4585                  ## NOTE: |</head> <link> |
4586                  #
4587                } else {
4588                  !!!cp ('t88.2');
4589                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4590                  ## NOTE: |</head> &#x20;|
4591                  #
4592                }
4593              } else {
4594                !!!cp ('t88.1');
4595                ## Ignore the token.
4596                #
4597              }
4598            unless (length $token->{data}) {            unless (length $token->{data}) {
4599                !!!cp ('t88');
4600              !!!next-token;              !!!next-token;
4601              redo B;              next B;
4602            }            }
4603    ## TODO: set $token->{column} appropriately
4604          }          }
4605    
4606          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4607              !!!cp ('t89');
4608            ## As if <head>            ## As if <head>
4609            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4610            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4611            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4612                  [$self->{head_element}, $el_category->{head}];
4613    
4614            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4615            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4616    
4617            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4618          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4619              !!!cp ('t90');
4620            ## As if </noscript>            ## As if </noscript>
4621            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4622            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4623                        
4624            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4625            ## As if </head>            ## As if </head>
# Line 2920  sub _tree_construction_main ($) { Line 4627  sub _tree_construction_main ($) {
4627    
4628            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4629          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4630              !!!cp ('t91');
4631            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4632    
4633            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4634            } else {
4635              !!!cp ('t92');
4636          }          }
4637    
4638              ## "after head" insertion mode          ## "after head" insertion mode
4639              ## As if <body>          ## As if <body>
4640              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4641              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4642              ## reprocess          ## reprocess
4643              redo B;          next B;
4644            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4645              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4646                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!cp ('t93');
4648                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4649                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];              $self->{open_elements}->[-1]->[0]->append_child
4650                  $self->{insertion_mode} = IN_HEAD_IM;                  ($self->{head_element});
4651                  !!!next-token;              push @{$self->{open_elements}},
4652                  redo B;                  [$self->{head_element}, $el_category->{head}];
4653                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4654                  #              !!!nack ('t93.1');
4655                } else {              !!!next-token;
4656                  !!!parse-error (type => 'in head:head'); # or in head noscript              next B;
4657                  ## Ignore the token            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4658                  !!!next-token;              !!!cp ('t93.2');
4659                  redo B;              !!!parse-error (type => 'after head', text => 'head',
4660                }                              token => $token);
4661              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              ## Ignore the token
4662                ## As if <head>              !!!nack ('t93.3');
4663                !!!create-element ($self->{head_element}, 'head');              !!!next-token;
4664                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              next B;
4665                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4666                !!!cp ('t95');
4667                !!!parse-error (type => 'in head:head',
4668                                token => $token); # or in head noscript
4669                ## Ignore the token
4670                !!!nack ('t95.1');
4671                !!!next-token;
4672                next B;
4673              }
4674            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4675              !!!cp ('t96');
4676              ## As if <head>
4677              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4678              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4679              push @{$self->{open_elements}},
4680                  [$self->{head_element}, $el_category->{head}];
4681    
4682                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4683                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4684              }          } else {
4685              !!!cp ('t97');
4686            }
4687    
4688              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4689                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4690                  ## As if </noscript>              !!!cp ('t98');
4691                  pop @{$self->{open_elements}};              ## As if </noscript>
4692                  !!!parse-error (type => 'in noscript:base');              pop @{$self->{open_elements}};
4693                              !!!parse-error (type => 'in noscript', text => 'base',
4694                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4695                  ## Reprocess in the "in head" insertion mode...            
4696                }              $self->{insertion_mode} = IN_HEAD_IM;
4697                ## Reprocess in the "in head" insertion mode...
4698              } else {
4699                !!!cp ('t99');
4700              }
4701    
4702                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4703                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4704                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t100');
4705                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4706                }                              text => $token->{tag_name}, token => $token);
4707                !!!insert-element ($token->{tag_name}, $token->{attributes});              push @{$self->{open_elements}},
4708                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  [$self->{head_element}, $el_category->{head}];
4709                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4710                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4711                !!!next-token;              !!!cp ('t101');
4712                redo B;            }
4713              } elsif ($token->{tag_name} eq 'link') {            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4714                ## NOTE: There is a "as if in head" code clone.            pop @{$self->{open_elements}};
4715                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}} # <head>
4716                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4717                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!nack ('t101.1');
4718                }            !!!next-token;
4719                !!!insert-element ($token->{tag_name}, $token->{attributes});            next B;
4720                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          } elsif ($token->{tag_name} eq 'link') {
4721                pop @{$self->{open_elements}}            ## NOTE: There is a "as if in head" code clone.
4722                    if $self->{insertion_mode} == AFTER_HEAD_IM;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4723                !!!next-token;              !!!cp ('t102');
4724                redo B;              !!!parse-error (type => 'after head',
4725              } elsif ($token->{tag_name} eq 'meta') {                              text => $token->{tag_name}, token => $token);
4726                ## NOTE: There is a "as if in head" code clone.              push @{$self->{open_elements}},
4727                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  [$self->{head_element}, $el_category->{head}];
4728                  !!!parse-error (type => 'after head:'.$token->{tag_name});              $self->{head_element_inserted} = 1;
4729                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4730                }              !!!cp ('t103');
4731                !!!insert-element ($token->{tag_name}, $token->{attributes});            }
4732                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4733              pop @{$self->{open_elements}};
4734              pop @{$self->{open_elements}} # <head>
4735                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4736              !!!ack ('t103.1');
4737              !!!next-token;
4738              next B;
4739            } elsif ($token->{tag_name} eq 'command' or
4740                     $token->{tag_name} eq 'eventsource') {
4741              if ($self->{insertion_mode} == IN_HEAD_IM) {
4742                ## NOTE: If the insertion mode at the time of the emission
4743                ## of the token was "before head", $self->{insertion_mode}
4744                ## is already changed to |IN_HEAD_IM|.
4745    
4746                ## NOTE: There is a "as if in head" code clone.
4747                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4748                pop @{$self->{open_elements}};
4749                pop @{$self->{open_elements}} # <head>
4750                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4751                !!!ack ('t103.2');
4752                !!!next-token;
4753                next B;
4754              } else {
4755                ## NOTE: "in head noscript" or "after head" insertion mode
4756                ## - in these cases, these tags are treated as same as
4757                ## normal in-body tags.
4758                !!!cp ('t103.3');
4759                #
4760              }
4761            } elsif ($token->{tag_name} eq 'meta') {
4762              ## NOTE: There is a "as if in head" code clone.
4763              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4764                !!!cp ('t104');
4765                !!!parse-error (type => 'after head',
4766                                text => $token->{tag_name}, token => $token);
4767                push @{$self->{open_elements}},
4768                    [$self->{head_element}, $el_category->{head}];
4769                $self->{head_element_inserted} = 1;
4770              } else {
4771                !!!cp ('t105');
4772              }
4773              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4774              my $meta_el = pop @{$self->{open_elements}};
4775    
4776                unless ($self->{confident}) {                unless ($self->{confident}) {
4777                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                  if ($token->{attributes}->{charset}) {
4778                      !!!cp ('t106');
4779                      ## NOTE: Whether the encoding is supported or not is handled
4780                      ## in the {change_encoding} callback.
4781                    $self->{change_encoding}                    $self->{change_encoding}
4782                        ->($self, $token->{attributes}->{charset}->{value});                        ->($self, $token->{attributes}->{charset}->{value},
4783                             $token);
4784                                        
4785                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4786                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4787                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4788                                                 ->{has_reference});                                                 ->{has_reference});
4789                  } elsif ($token->{attributes}->{content}) {                  } elsif ($token->{attributes}->{content}) {
                   ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
4790                    if ($token->{attributes}->{content}->{value}                    if ($token->{attributes}->{content}->{value}
4791                        =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4792                            [\x09-\x0D\x20]*=                            [\x09\x0A\x0C\x0D\x20]*=
4793                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4794                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                            ([^"'\x09\x0A\x0C\x0D\x20]
4795                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4796                        !!!cp ('t107');
4797                        ## NOTE: Whether the encoding is supported or not is handled
4798                        ## in the {change_encoding} callback.
4799                      $self->{change_encoding}                      $self->{change_encoding}
4800                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4801                               $token);
4802                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4803                          ->set_user_data (manakai_has_reference =>                          ->set_user_data (manakai_has_reference =>
4804                                               $token->{attributes}->{content}                                               $token->{attributes}->{content}
4805                                                     ->{has_reference});                                                     ->{has_reference});
4806                      } else {
4807                        !!!cp ('t108');
4808                    }                    }
4809                  }                  }
4810                } else {                } else {
4811                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
4812                      !!!cp ('t109');
4813                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4814                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4815                                             $token->{attributes}->{charset}                                             $token->{attributes}->{charset}
4816                                                 ->{has_reference});                                                 ->{has_reference});
4817                  }                  }
4818                  if ($token->{attributes}->{content}) {                  if ($token->{attributes}->{content}) {
4819                      !!!cp ('t110');
4820                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4821                        ->set_user_data (manakai_has_reference =>                        ->set_user_data (manakai_has_reference =>
4822                                             $token->{attributes}->{content}                                             $token->{attributes}->{content}
# Line 3039  sub _tree_construction_main ($) { Line 4824  sub _tree_construction_main ($) {
4824                  }                  }
4825                }                }
4826    
4827                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4828                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4829                  !!!ack ('t110.1');
4830                !!!next-token;                !!!next-token;
4831                redo B;                next B;
4832              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4833                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4834                  ## As if </noscript>              !!!cp ('t111');
4835                  pop @{$self->{open_elements}};              ## As if </noscript>
4836                  !!!parse-error (type => 'in noscript:title');              pop @{$self->{open_elements}};
4837                              !!!parse-error (type => 'in noscript', text => 'title',
4838                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4839                  ## Reprocess in the "in head" insertion mode...            
4840                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4841                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4842                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4843                }              !!!cp ('t112');
4844                !!!parse-error (type => 'after head',
4845                                text => $token->{tag_name}, token => $token);
4846                push @{$self->{open_elements}},
4847                    [$self->{head_element}, $el_category->{head}];
4848                $self->{head_element_inserted} = 1;
4849              } else {
4850                !!!cp ('t113');
4851              }
4852    
4853                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4854                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4855                    : $self->{open_elements}->[-1]->[0];            ## ISSUE: A spec bug [Bug 6038]
4856                $parse_rcdata->(RCDATA_CONTENT_MODEL,            splice @{$self->{open_elements}}, -2, 1, () # <head>
4857                                sub { $parent->append_child ($_[0]) });                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4858                pop @{$self->{open_elements}}            next B;
4859                    if $self->{insertion_mode} == AFTER_HEAD_IM;          } elsif ($token->{tag_name} eq 'style' or
4860                redo B;                   $token->{tag_name} eq 'noframes') {
4861              } elsif ($token->{tag_name} eq 'style') {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4862                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## insertion mode IN_HEAD_IM)
4863                ## insertion mode IN_HEAD_IM)            ## NOTE: There is a "as if in head" code clone.
4864                ## NOTE: There is a "as if in head" code clone.            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4865                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!cp ('t114');
4866                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4867                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4868                }              push @{$self->{open_elements}},
4869                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                  [$self->{head_element}, $el_category->{head}];
4870                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4871                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4872                redo B;              !!!cp ('t115');
4873              } elsif ($token->{tag_name} eq 'noscript') {            }
4874              $parse_rcdata->(CDATA_CONTENT_MODEL);
4875              ## ISSUE: A spec bug [Bug 6038]
4876              splice @{$self->{open_elements}}, -2, 1, () # <head>
4877                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4878              next B;
4879            } elsif ($token->{tag_name} eq 'noscript') {
4880                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4881                    !!!cp ('t116');
4882                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4883                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4884                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4885                    !!!nack ('t116.1');
4886                  !!!next-token;                  !!!next-token;
4887                  redo B;                  next B;
4888                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4889                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4890                    !!!parse-error (type => 'in noscript', text => 'noscript',
4891                                    token => $token);
4892                  ## Ignore the token                  ## Ignore the token
4893                    !!!nack ('t117.1');
4894                  !!!next-token;                  !!!next-token;
4895                  redo B;                  next B;
4896                } else {                } else {
4897                    !!!cp ('t118');
4898                  #                  #
4899                }                }
4900              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4901                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4902                  ## As if </noscript>              !!!cp ('t119');
4903                  pop @{$self->{open_elements}};              ## As if </noscript>
4904                  !!!parse-error (type => 'in noscript:script');              pop @{$self->{open_elements}};
4905                              !!!parse-error (type => 'in noscript', text => 'script',
4906                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4907                  ## Reprocess in the "in head" insertion mode...            
4908                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4909                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4910                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4911                }              !!!cp ('t120');
4912                !!!parse-error (type => 'after head',
4913                                text => $token->{tag_name}, token => $token);
4914                push @{$self->{open_elements}},
4915                    [$self->{head_element}, $el_category->{head}];
4916                $self->{head_element_inserted} = 1;
4917              } else {
4918                !!!cp ('t121');
4919              }
4920    
4921                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4922                $script_start_tag->($insert_to_current);            $script_start_tag->();
4923                pop @{$self->{open_elements}}            ## ISSUE: A spec bug  [Bug 6038]
4924                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
4925                redo B;                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4926              } elsif ($token->{tag_name} eq 'body' or            next B;
4927                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
4928                     $token->{tag_name} eq 'frameset') {
4929                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4930                    !!!cp ('t122');
4931                  ## As if </noscript>                  ## As if </noscript>
4932                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4933                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4934                                    text => $token->{tag_name}, token => $token);
4935                                    
4936                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4937                  ## As if </head>                  ## As if </head>
# Line 3122  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939                                    
4940                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4941                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4942                    !!!cp ('t124');
4943                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4944                                    
4945                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4946                  } else {
4947                    !!!cp ('t125');
4948                }                }
4949    
4950                ## "after head" insertion mode                ## "after head" insertion mode
4951                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4952                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4953                    !!!cp ('t126');
4954                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
4955                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
4956                    !!!cp ('t127');
4957                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4958                } else {                } else {
4959                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4960                }                }
4961                  !!!nack ('t127.1');
4962                !!!next-token;                !!!next-token;
4963                redo B;                next B;
4964              } else {              } else {
4965                  !!!cp ('t128');
4966                #                #
4967              }              }
4968    
4969              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4970                  !!!cp ('t129');
4971                ## As if </noscript>                ## As if </noscript>
4972                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4973                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4974                                  text => $token->{tag_name}, token => $token);
4975                                
4976                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4977                ## As if </head>                ## As if </head>
# Line 3153  sub _tree_construction_main ($) { Line 4979  sub _tree_construction_main ($) {
4979    
4980                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4981              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4982                  !!!cp ('t130');
4983                ## As if </head>                ## As if </head>
4984                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4985    
4986                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4987                } else {
4988                  !!!cp ('t131');
4989              }              }
4990    
4991              ## "after head" insertion mode              ## "after head" insertion mode
4992              ## As if <body>              ## As if <body>
4993              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4994              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4995              ## reprocess              ## reprocess
4996              redo B;              !!!ack-later;
4997                next B;
4998            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4999              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
5000                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5001                    !!!cp ('t132');
5002                  ## As if <head>                  ## As if <head>
5003                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5004                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5005                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
5006                        [$self->{head_element}, $el_category->{head}];
5007    
5008                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5009                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5010                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5011                  !!!next-token;                  !!!next-token;
5012                  redo B;                  next B;
5013                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5014                    !!!cp ('t133');
5015                  ## As if </noscript>                  ## As if </noscript>
5016                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5017                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/',
5018                                    text => 'head', token => $token);
5019                                    
5020                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5021                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5022                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5023                  !!!next-token;                  !!!next-token;
5024                  redo B;                  next B;
5025                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5026                    !!!cp ('t134');
5027                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5028                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5029                  !!!next-token;                  !!!next-token;
5030                  redo B;                  next B;
5031                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5032                    !!!cp ('t134.1');
5033                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5034                                    token => $token);
5035                    ## Ignore the token
5036                    !!!next-token;
5037                    next B;
5038                } else {                } else {
5039                  #                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5040                }                }
5041              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5042                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5043                    !!!cp ('t136');
5044                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5045                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5046                  !!!next-token;                  !!!next-token;
5047                  redo B;                  next B;
5048                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5049                  !!!parse-error (type => 'unmatched end tag:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
5050                    !!!cp ('t137');
5051                    !!!parse-error (type => 'unmatched end tag',
5052                                    text => 'noscript', token => $token);
5053                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5054                  !!!next-token;                  !!!next-token;
5055                  redo B;                  next B;
5056                } else {                } else {
5057                    !!!cp ('t138');
5058                  #                  #
5059                }                }
5060              } elsif ({              } elsif ({
5061                        body => 1, html => 1,                        body => 1, html => 1,
5062                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5063                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5064                  ## As if <head>                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5065                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_IM or
5066                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5067                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!cp ('t140');
5068                    !!!parse-error (type => 'unmatched end tag',
5069                  $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});  
5070                  ## Ignore the token                  ## Ignore the token
5071                  !!!next-token;                  !!!next-token;
5072                  redo B;                  next B;
5073                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5074                    !!!cp ('t140.1');
5075                    !!!parse-error (type => 'unmatched end tag',
5076                                    text => $token->{tag_name}, token => $token);
5077                    ## Ignore the token
5078                    !!!next-token;
5079                    next B;
5080                  } else {
5081                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5082                }                }
5083                              } elsif ($token->{tag_name} eq 'p') {
5084                #                !!!cp ('t142');
5085              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5086                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5087                       }->{$token->{tag_name}}) {                ## Ignore the token
5088                  !!!next-token;
5089                  next B;
5090                } elsif ($token->{tag_name} eq 'br') {
5091                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5092                  ## As if <head>                  !!!cp ('t142.2');
5093                  !!!create-element ($self->{head_element}, 'head');                  ## (before head) as if <head>, (in head) as if </head>
5094                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5095                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5096                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5097      
5098                    ## Reprocess in the "after head" insertion mode...
5099                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5100                    !!!cp ('t143.2');
5101                    ## As if </head>
5102                    pop @{$self->{open_elements}};
5103                    $self->{insertion_mode} = AFTER_HEAD_IM;
5104      
5105                    ## Reprocess in the "after head" insertion mode...
5106                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5107                    !!!cp ('t143.3');
5108                    ## ISSUE: Two parse errors for <head><noscript></br>
5109                    !!!parse-error (type => 'unmatched end tag',
5110                                    text => 'br', token => $token);
5111                    ## As if </noscript>
5112                    pop @{$self->{open_elements}};
5113                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5114    
5115                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5116                }                  ## As if </head>
5117                    pop @{$self->{open_elements}};
5118                    $self->{insertion_mode} = AFTER_HEAD_IM;
5119    
5120                #                  ## Reprocess in the "after head" insertion mode...
5121              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5122                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
5123                  #                  #
5124                } else {                } else {
5125                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5126                }                }
5127    
5128                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5129                  !!!parse-error (type => 'unmatched end tag',
5130                                  text => 'br', token => $token);
5131                  ## Ignore the token
5132                  !!!next-token;
5133                  next B;
5134                } else {
5135                  !!!cp ('t145');
5136                  !!!parse-error (type => 'unmatched end tag',
5137                                  text => $token->{tag_name}, token => $token);
5138                  ## Ignore the token
5139                  !!!next-token;
5140                  next B;
5141              }              }
5142    
5143              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5144                  !!!cp ('t146');
5145                ## As if </noscript>                ## As if </noscript>
5146                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5147                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5148                                  text => $token->{tag_name}, token => $token);
5149                                
5150                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5151                ## As if </head>                ## As if </head>
# Line 3265  sub _tree_construction_main ($) { Line 5153  sub _tree_construction_main ($) {
5153    
5154                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5155              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5156                  !!!cp ('t147');
5157                ## As if </head>                ## As if </head>
5158                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5159    
5160                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5161              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5162                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
5163                  !!!cp ('t148');
5164                  !!!parse-error (type => 'unmatched end tag',
5165                                  text => $token->{tag_name}, token => $token);
5166                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5167                !!!next-token;                !!!next-token;
5168                redo B;                next B;
5169                } else {
5170                  !!!cp ('t149');
5171              }              }
5172    
5173              ## "after head" insertion mode              ## "after head" insertion mode
5174              ## As if <body>              ## As if <body>
5175              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5176              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5177              ## reprocess              ## reprocess
5178              redo B;              next B;
5179            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5180              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5181            }            !!!cp ('t149.1');
5182    
5183              ## NOTE: As if <head>
5184              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5185              $self->{open_elements}->[-1]->[0]->append_child
5186                  ($self->{head_element});
5187              #push @{$self->{open_elements}},
5188              #    [$self->{head_element}, $el_category->{head}];
5189              #$self->{insertion_mode} = IN_HEAD_IM;
5190              ## NOTE: Reprocess.
5191    
5192              ## NOTE: As if </head>
5193              #pop @{$self->{open_elements}};
5194              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5195              ## NOTE: Reprocess.
5196              
5197              #
5198            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5199              !!!cp ('t149.2');
5200    
5201              ## NOTE: As if </head>
5202              pop @{$self->{open_elements}};
5203              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5204              ## NOTE: Reprocess.
5205    
5206              #
5207            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5208              !!!cp ('t149.3');
5209    
5210              !!!parse-error (type => 'in noscript:#eof', token => $token);
5211    
5212              ## As if </noscript>
5213              pop @{$self->{open_elements}};
5214              #$self->{insertion_mode} = IN_HEAD_IM;
5215              ## NOTE: Reprocess.
5216    
5217              ## NOTE: As if </head>
5218              pop @{$self->{open_elements}};
5219              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5220              ## NOTE: Reprocess.
5221    
5222              #
5223            } else {
5224              !!!cp ('t149.4');
5225              #
5226            }
5227    
5228            ## ISSUE: An issue in the spec.          ## NOTE: As if <body>
5229            !!!insert-element ('body',, $token);
5230            $self->{insertion_mode} = IN_BODY_IM;
5231            ## NOTE: Reprocess.
5232            next B;
5233          } else {
5234            die "$0: $token->{type}: Unknown token type";
5235          }
5236      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5237            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5238                !!!cp ('t150');
5239              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5240              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5241                            
5242              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5243    
5244              !!!next-token;              !!!next-token;
5245              redo B;              next B;
5246            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5247              if ({              if ({
5248                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 3303  sub _tree_construction_main ($) { Line 5250  sub _tree_construction_main ($) {
5250                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5251                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5252                  ## have an element in table scope                  ## have an element in table scope
5253                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5254                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5255                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] == TABLE_CELL_EL) {
5256                      $tn = $node->[1];                      !!!cp ('t151');
5257                      last INSCOPE;  
5258                    } elsif ({                      ## Close the cell
5259                              table => 1, html => 1,                      !!!back-token; # <x>
5260                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5261                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5262                    }                                line => $token->{line},
5263                  } # INSCOPE                                column => $token->{column}};
5264                    unless (defined $tn) {                      next B;
5265                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5266                      ## Ignore the token                      !!!cp ('t152');
5267                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5268                      redo B;                      last;
5269                    }                    }
5270                                    }
5271                  ## Close the cell  
5272                  !!!back-token; # <?>                  !!!cp ('t153');
5273                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
5274                  redo B;                      text => $token->{tag_name}, token => $token);
5275                    ## Ignore the token
5276                    !!!nack ('t153.1');
5277                    !!!next-token;
5278                    next B;
5279                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5280                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5281                                    token => $token);
5282                                    
5283                  ## As if </caption>                  ## NOTE: As if </caption>.
5284                  ## have a table element in table scope                  ## have a table element in table scope
5285                  my $i;                  my $i;
5286                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5287                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5288                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5289                      $i = $_;                      if ($node->[1] == CAPTION_EL) {
5290                      last INSCOPE;                        !!!cp ('t155');
5291                    } elsif ({                        $i = $_;
5292                              table => 1, html => 1,                        last INSCOPE;
5293                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5294                      last INSCOPE;                        !!!cp ('t156');
5295                          last;
5296                        }
5297                    }                    }
5298    
5299                      !!!cp ('t157');
5300                      !!!parse-error (type => 'start tag not allowed',
5301                                      text => $token->{tag_name}, token => $token);
5302                      ## Ignore the token
5303                      !!!nack ('t157.1');
5304                      !!!next-token;
5305                      next B;
5306                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5307                                    
5308                  ## generate implied end tags                  ## generate implied end tags
5309                  if ({                  while ($self->{open_elements}->[-1]->[1]
5310                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5311                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5312                       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;  
5313                  }                  }
5314    
5315                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5316                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5317                      !!!parse-error (type => 'not closed',
5318                                      text => $self->{open_elements}->[-1]->[0]
5319                                          ->manakai_local_name,
5320                                      token => $token);
5321                    } else {
5322                      !!!cp ('t160');
5323                  }                  }
5324                                    
5325                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3375  sub _tree_construction_main ($) { Line 5329  sub _tree_construction_main ($) {
5329                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5330                                    
5331                  ## reprocess                  ## reprocess
5332                  redo B;                  !!!ack-later;
5333                    next B;
5334                } else {                } else {
5335                    !!!cp ('t161');
5336                  #                  #
5337                }                }
5338              } else {              } else {
5339                  !!!cp ('t162');
5340                #                #
5341              }              }
5342            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3389  sub _tree_construction_main ($) { Line 5346  sub _tree_construction_main ($) {
5346                  my $i;                  my $i;
5347                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5348                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5349                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5350                        !!!cp ('t163');
5351                      $i = $_;                      $i = $_;
5352                      last INSCOPE;                      last INSCOPE;
5353                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5354                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5355                      last INSCOPE;                      last INSCOPE;
5356                    }                    }
5357                  } # INSCOPE                  } # INSCOPE
5358                    unless (defined $i) {                    unless (defined $i) {
5359                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5360                        !!!parse-error (type => 'unmatched end tag',
5361                                        text => $token->{tag_name},
5362                                        token => $token);
5363                      ## Ignore the token                      ## Ignore the token
5364                      !!!next-token;                      !!!next-token;
5365                      redo B;                      next B;
5366                    }                    }
5367                                    
5368                  ## generate implied end tags                  ## generate implied end tags
5369                  if ({                  while ($self->{open_elements}->[-1]->[1]
5370                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5371                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5372                       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;  
5373                  }                  }
5374                    
5375                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5376                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5377                      !!!cp ('t167');
5378                      !!!parse-error (type => 'not closed',
5379                                      text => $self->{open_elements}->[-1]->[0]
5380                                          ->manakai_local_name,
5381                                      token => $token);
5382                    } else {
5383                      !!!cp ('t168');
5384                  }                  }
5385                                    
5386                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3430  sub _tree_construction_main ($) { Line 5390  sub _tree_construction_main ($) {
5390                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5391                                    
5392                  !!!next-token;                  !!!next-token;
5393                  redo B;                  next B;
5394                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5395                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5396                    !!!parse-error (type => 'unmatched end tag',
5397                                    text => $token->{tag_name}, token => $token);
5398                  ## Ignore the token                  ## Ignore the token
5399                  !!!next-token;                  !!!next-token;
5400                  redo B;                  next B;
5401                } else {                } else {
5402                    !!!cp ('t170');
5403                  #                  #
5404                }                }
5405              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5406                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5407                  ## have a table element in table scope                  ## have a table element in table scope
5408                  my $i;                  my $i;
5409                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5410                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5411                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5412                      $i = $_;                      if ($node->[1] == CAPTION_EL) {
5413                      last INSCOPE;                        !!!cp ('t171');
5414                    } elsif ({                        $i = $_;
5415                              table => 1, html => 1,                        last INSCOPE;
5416                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5417                      last INSCOPE;                        !!!cp ('t172');
5418                          last;
5419                        }
5420                    }                    }
5421    
5422                      !!!cp ('t173');
5423                      !!!parse-error (type => 'unmatched end tag',
5424                                      text => $token->{tag_name}, token => $token);
5425                      ## Ignore the token
5426                      !!!next-token;
5427                      next B;
5428                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5429                                    
5430                  ## generate implied end tags                  ## generate implied end tags
5431                  if ({                  while ($self->{open_elements}->[-1]->[1]
5432                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5433                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5434                       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;  
5435                  }                  }
5436                                    
5437                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5438                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5439                      !!!parse-error (type => 'not closed',
5440                                      text => $self->{open_elements}->[-1]->[0]
5441                                          ->manakai_local_name,
5442                                      token => $token);
5443                    } else {
5444                      !!!cp ('t176');
5445                  }                  }
5446                                    
5447                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3484  sub _tree_construction_main ($) { Line 5451  sub _tree_construction_main ($) {
5451                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5452                                    
5453                  !!!next-token;                  !!!next-token;
5454                  redo B;                  next B;
5455                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5456                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5457                    !!!parse-error (type => 'unmatched end tag',
5458                                    text => $token->{tag_name}, token => $token);
5459                  ## Ignore the token                  ## Ignore the token
5460                  !!!next-token;                  !!!next-token;
5461                  redo B;                  next B;
5462                } else {                } else {
5463                    !!!cp ('t178');
5464                  #                  #
5465                }                }
5466              } elsif ({              } elsif ({
# Line 3501  sub _tree_construction_main ($) { Line 5471  sub _tree_construction_main ($) {
5471                ## have an element in table scope                ## have an element in table scope
5472                my $i;                my $i;
5473                my $tn;                my $tn;
5474                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5475                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5476                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5477                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5478                    last INSCOPE;                      !!!cp ('t179');
5479                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5480                    $tn = $node->[1];  
5481                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5482                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5483                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5484                            table => 1, html => 1,                                line => $token->{line},
5485                           }->{$node->[1]}) {                                column => $token->{column}};
5486                    last INSCOPE;                      next B;
5487                      } elsif ($node->[1] == TABLE_CELL_EL) {
5488                        !!!cp ('t180');
5489                        $tn = $node->[0]->manakai_local_name;
5490                        ## NOTE: There is exactly one |td| or |th| element
5491                        ## in scope in the stack of open elements by definition.
5492                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5493                        ## ISSUE: Can this be reached?
5494                        !!!cp ('t181');
5495                        last;
5496                      }
5497                  }                  }
5498                } # INSCOPE  
5499                unless (defined $i) {                  !!!cp ('t182');
5500                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5501                        text => $token->{tag_name}, token => $token);
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  redo B;                  next B;
5505                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5506              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5507                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5508                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5509                                  token => $token);
5510    
5511                ## As if </caption>                ## As if </caption>
5512                ## have a table element in table scope                ## have a table element in table scope
5513                my $i;                my $i;
5514                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5515                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5516                  if ($node->[1] eq 'caption') {                  if ($node->[1] == CAPTION_EL) {
5517                      !!!cp ('t184');
5518                    $i = $_;                    $i = $_;
5519                    last INSCOPE;                    last INSCOPE;
5520                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5521                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5522                    last INSCOPE;                    last INSCOPE;
5523                  }                  }
5524                } # INSCOPE                } # INSCOPE
5525                unless (defined $i) {                unless (defined $i) {
5526                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5527                    !!!parse-error (type => 'unmatched end tag',
5528                                    text => 'caption', token => $token);
5529                  ## Ignore the token                  ## Ignore the token
5530                  !!!next-token;                  !!!next-token;
5531                  redo B;                  next B;
5532                }                }
5533                                
5534                ## generate implied end tags                ## generate implied end tags
5535                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5536                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5537                     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;  
5538                }                }
5539    
5540                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5541                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5542                    !!!parse-error (type => 'not closed',
5543                                    text => $self->{open_elements}->[-1]->[0]
5544                                        ->manakai_local_name,
5545                                    token => $token);
5546                  } else {
5547                    !!!cp ('t189');
5548                }                }
5549    
5550                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3577  sub _tree_construction_main ($) { Line 5554  sub _tree_construction_main ($) {
5554                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5555    
5556                ## reprocess                ## reprocess
5557                redo B;                next B;
5558              } elsif ({              } elsif ({
5559                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5560                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5561                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5562                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
5563                    !!!parse-error (type => 'unmatched end tag',
5564                                    text => $token->{tag_name}, token => $token);
5565                  ## Ignore the token                  ## Ignore the token
5566                  !!!next-token;                  !!!next-token;
5567                  redo B;                  next B;
5568                } else {                } else {
5569                    !!!cp ('t191');
5570                  #                  #
5571                }                }
5572              } elsif ({              } elsif ({
# Line 3594  sub _tree_construction_main ($) { Line 5574  sub _tree_construction_main ($) {
5574                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5575                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5576                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5577                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5578                  !!!parse-error (type => 'unmatched end tag',
5579                                  text => $token->{tag_name}, token => $token);
5580                ## Ignore the token                ## Ignore the token
5581                !!!next-token;                !!!next-token;
5582                redo B;                next B;
5583              } else {              } else {
5584                  !!!cp ('t193');
5585                #                #
5586              }              }
5587          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5588            for my $entry (@{$self->{open_elements}}) {
5589              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5590                !!!cp ('t75');
5591                !!!parse-error (type => 'in body:#eof', token => $token);
5592                last;
5593              }
5594            }
5595    
5596            ## Stop parsing.
5597            last B;
5598        } else {        } else {
5599          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5600        }        }
# Line 3609  sub _tree_construction_main ($) { Line 5603  sub _tree_construction_main ($) {
5603        #        #
5604      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5605        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5606              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if (not $open_tables->[-1]->[1] and # tainted
5607                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5608              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5609                                
5610                unless (length $token->{data}) {            unless (length $token->{data}) {
5611                  !!!next-token;              !!!cp ('t194');
5612                  redo B;              !!!next-token;
5613                }              next B;
5614              }            } else {
5615                !!!cp ('t195');
5616              }
5617            }
5618    
5619              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5620    
5621              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5622              ## 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);  
5623                            
5624              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5625                   table => 1, tbody => 1, tfoot => 1,            # MUST
5626                   thead => 1, tr => 1,            my $foster_parent_element;
5627                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5628                # MUST            my $prev_sibling;
5629                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5630                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5631                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5632                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5633                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5634                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5635                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5636                      $foster_parent_element = $parent;                  $prev_sibling = $next_sibling->previous_sibling;
5637                      $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});  
5638                } else {                } else {
5639                  $foster_parent_element->insert_before                  !!!cp ('t197');
5640                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5641                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5642                    #
5643                }                }
5644              } else {                last OE;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5645              }              }
5646              } # OE
5647              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5648              $prev_sibling = $foster_parent_element->last_child
5649                  unless defined $foster_parent_element;
5650              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5651              if (defined $prev_sibling and
5652                  $prev_sibling->node_type == 3) {
5653                !!!cp ('t198');
5654                $prev_sibling->manakai_append_text ($token->{data});
5655              } else {
5656                !!!cp ('t199');
5657                $foster_parent_element->insert_before
5658                    ($self->{document}->create_text_node ($token->{data}),
5659                     $next_sibling);
5660              }
5661              $open_tables->[-1]->[1] = 1; # tainted
5662              $open_tables->[-1]->[2] = 1; # ~node inserted
5663            } else {
5664              ## NOTE: Fragment case or in a foster parent'ed element
5665              ## (e.g. |<table><span>a|).  In fragment case, whether the
5666              ## character is appended to existing node or a new node is
5667              ## created is irrelevant, since the foster parent'ed nodes
5668              ## are discarded and fragment parsing does not invoke any
5669              ## script.
5670              !!!cp ('t200');
5671              $self->{open_elements}->[-1]->[0]->manakai_append_text
5672                  ($token->{data});
5673            }
5674                            
5675              !!!next-token;          !!!next-token;
5676              redo B;          next B;
5677        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5678              if ({          if ({
5679                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5680                   th => 1, td => 1,               th => 1, td => 1,
5681                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5682                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5683                  ## Clear back to table context              ## Clear back to table context
5684                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5685                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5686                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5687                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5688                  }              }
5689                                
5690                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5691                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5692                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5693                }            }
5694              
5695                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5696                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5697                    !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t202');
5698                  }                !!!parse-error (type => 'missing start tag:tr', token => $token);
5699                }
5700                                    
5701                  ## Clear back to table body context              ## Clear back to table body context
5702                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5703                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5704                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5705                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## ISSUE: Can this case be reached?
5706                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5707                  }              }
5708                                    
5709                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5710                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5711                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t204');
5712                    !!!next-token;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5713                    redo B;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5714                  } else {                !!!nack ('t204');
5715                    !!!insert-element ('tr');                !!!next-token;
5716                    ## reprocess in the "in row" insertion mode                next B;
5717                  }              } else {
5718                }                !!!cp ('t205');
5719                  !!!insert-element ('tr',, $token);
5720                  ## reprocess in the "in row" insertion mode
5721                }
5722              } else {
5723                !!!cp ('t206');
5724              }
5725    
5726                ## Clear back to table row context                ## Clear back to table row context
5727                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5728                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5729                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5730                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5731                }                }
5732                                
5733                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5734                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5735              $self->{insertion_mode} = IN_CELL_IM;
5736    
5737                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5738                                
5739              !!!nack ('t207.1');
5740              !!!next-token;
5741              next B;
5742            } elsif ({
5743                      caption => 1, col => 1, colgroup => 1,
5744                      tbody => 1, tfoot => 1, thead => 1,
5745                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5746                     }->{$token->{tag_name}}) {
5747              if ($self->{insertion_mode} == IN_ROW_IM) {
5748                ## As if </tr>
5749                ## have an element in table scope
5750                my $i;
5751                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5752                  my $node = $self->{open_elements}->[$_];
5753                  if ($node->[1] == TABLE_ROW_EL) {
5754                    !!!cp ('t208');
5755                    $i = $_;
5756                    last INSCOPE;
5757                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5758                    !!!cp ('t209');
5759                    last INSCOPE;
5760                  }
5761                } # INSCOPE
5762                unless (defined $i) {
5763                  !!!cp ('t210');
5764                  ## TODO: This type is wrong.
5765                  !!!parse-error (type => 'unmacthed end tag',
5766                                  text => $token->{tag_name}, token => $token);
5767                  ## Ignore the token
5768                  !!!nack ('t210.1');
5769                !!!next-token;                !!!next-token;
5770                redo B;                next B;
5771              } 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;  
                 }  
5772                                    
5773                  ## Clear back to table row context                  ## Clear back to table row context
5774                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5775                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5776                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
5777                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
5778                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5779                  }                  }
5780                                    
5781                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
5782                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5783                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5784                      !!!cp ('t212');
5785                    ## reprocess                    ## reprocess
5786                    redo B;                    !!!ack-later;
5787                      next B;
5788                  } else {                  } else {
5789                      !!!cp ('t213');
5790                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
5791                  }                  }
5792                }                }
# Line 3772  sub _tree_construction_main ($) { Line 5796  sub _tree_construction_main ($) {
5796                  my $i;                  my $i;
5797                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5798                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5799                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
5800                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
5801                      $i = $_;                      $i = $_;
5802                      last INSCOPE;                      last INSCOPE;
5803                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5804                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
5805                      last INSCOPE;                      last INSCOPE;
5806                    }                    }
5807                  } # INSCOPE                  } # INSCOPE
5808                  unless (defined $i) {                  unless (defined $i) {
5809                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
5810    ## TODO: This erorr type is wrong.
5811                      !!!parse-error (type => 'unmatched end tag',
5812                                      text => $token->{tag_name}, token => $token);
5813                    ## Ignore the token                    ## Ignore the token
5814                      !!!nack ('t216.1');
5815                    !!!next-token;                    !!!next-token;
5816                    redo B;                    next B;
5817                  }                  }
5818    
5819                  ## Clear back to table body context                  ## Clear back to table body context
5820                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5821                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5822                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5823                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5824                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5825                  }                  }
5826                                    
# Line 3808  sub _tree_construction_main ($) { Line 5834  sub _tree_construction_main ($) {
5834                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5835                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5836                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
5837                  } else {
5838                    !!!cp ('t218');
5839                }                }
5840    
5841                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5842                  ## Clear back to table context              ## Clear back to table context
5843                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5844                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5845                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t219');
5846                    pop @{$self->{open_elements}};                ## ISSUE: Can this state be reached?
5847                  }                pop @{$self->{open_elements}};
5848                                }
5849                  !!!insert-element ('colgroup');              
5850                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              !!!insert-element ('colgroup',, $token);
5851                  ## reprocess              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5852                  redo B;              ## reprocess
5853                } elsif ({              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5854                          caption => 1,              !!!ack-later;
5855                          colgroup => 1,              next B;
5856                          tbody => 1, tfoot => 1, thead => 1,            } elsif ({
5857                         }->{$token->{tag_name}}) {                      caption => 1,
5858                  ## Clear back to table context                      colgroup => 1,
5859                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                      tbody => 1, tfoot => 1, thead => 1,
5860                         $self->{open_elements}->[-1]->[1] ne 'html') {                     }->{$token->{tag_name}}) {
5861                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## Clear back to table context
5862                    while (not ($self->{open_elements}->[-1]->[1]
5863                                    & TABLE_SCOPING_EL)) {
5864                      !!!cp ('t220');
5865                      ## ISSUE: Can this state be reached?
5866                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5867                  }                  }
5868                                    
5869                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5870                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5871                                    
5872                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5873                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5874                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5875                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5876                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5877                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5878                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5879                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5880                  !!!next-token;                                        }->{$token->{tag_name}};
5881                  redo B;              !!!next-token;
5882                } else {              !!!nack ('t220.1');
5883                  die "$0: in table: <>: $token->{tag_name}";              next B;
5884                }            } else {
5885                die "$0: in table: <>: $token->{tag_name}";
5886              }
5887              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5888                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
5889                                  text => $self->{open_elements}->[-1]->[0]
5890                                      ->manakai_local_name,
5891                                  token => $token);
5892    
5893                ## As if </table>                ## As if </table>
5894                ## have a table element in table scope                ## have a table element in table scope
5895                my $i;                my $i;
5896                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5897                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5898                  if ($node->[1] eq 'table') {                  if ($node->[1] == TABLE_EL) {
5899                      !!!cp ('t221');
5900                    $i = $_;                    $i = $_;
5901                    last INSCOPE;                    last INSCOPE;
5902                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5903                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5904                    last INSCOPE;                    last INSCOPE;
5905                  }                  }
5906                } # INSCOPE                } # INSCOPE
5907                unless (defined $i) {                unless (defined $i) {
5908                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5909    ## TODO: The following is wrong, maybe.
5910                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5911                                    token => $token);
5912                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5913                    !!!nack ('t223.1');
5914                  !!!next-token;                  !!!next-token;
5915                  redo B;                  next B;
5916                }                }
5917                                
5918    ## TODO: Followings are removed from the latest spec.
5919                ## generate implied end tags                ## generate implied end tags
5920                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5921                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5922                     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;  
5923                }                }
5924    
5925                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5926                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5927                    ## NOTE: |<table><tr><table>|
5928                    !!!parse-error (type => 'not closed',
5929                                    text => $self->{open_elements}->[-1]->[0]
5930                                        ->manakai_local_name,
5931                                    token => $token);
5932                  } else {
5933                    !!!cp ('t226');
5934                }                }
5935    
5936                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5937                  pop @{$open_tables};
5938    
5939                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5940    
5941                ## reprocess            ## reprocess
5942                redo B;            !!!ack-later;
5943          } else {            next B;
5944            !!!parse-error (type => 'in table:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'style') {
5945              if (not $open_tables->[-1]->[1]) { # tainted
5946                !!!cp ('t227.8');
5947                ## NOTE: This is a "as if in head" code clone.
5948                $parse_rcdata->(CDATA_CONTENT_MODEL);
5949                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950                next B;
5951              } else {
5952                !!!cp ('t227.7');
5953                #
5954              }
5955            } elsif ($token->{tag_name} eq 'script') {
5956              if (not $open_tables->[-1]->[1]) { # tainted
5957                !!!cp ('t227.6');
5958                ## NOTE: This is a "as if in head" code clone.
5959                $script_start_tag->();
5960                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5961                next B;
5962              } else {
5963                !!!cp ('t227.5');
5964                #
5965              }
5966            } elsif ($token->{tag_name} eq 'input') {
5967              if (not $open_tables->[-1]->[1]) { # tainted
5968                if ($token->{attributes}->{type}) { ## TODO: case
5969                  my $type = lc $token->{attributes}->{type}->{value};
5970                  if ($type eq 'hidden') {
5971                    !!!cp ('t227.3');
5972                    !!!parse-error (type => 'in table',
5973                                    text => $token->{tag_name}, token => $token);
5974    
5975            $insert = $insert_to_foster;                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5976                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5977    
5978                    ## TODO: form element pointer
5979    
5980                    pop @{$self->{open_elements}};
5981    
5982                    !!!next-token;
5983                    !!!ack ('t227.2.1');
5984                    next B;
5985                  } else {
5986                    !!!cp ('t227.2');
5987                    #
5988                  }
5989                } else {
5990                  !!!cp ('t227.1');
5991                  #
5992                }
5993              } else {
5994                !!!cp ('t227.4');
5995                #
5996              }
5997            } else {
5998              !!!cp ('t227');
5999            #            #
6000          }          }
6001    
6002            !!!parse-error (type => 'in table', text => $token->{tag_name},
6003                            token => $token);
6004    
6005            $insert = $insert_to_foster;
6006            #
6007        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6008              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
6009                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
# Line 3911  sub _tree_construction_main ($) { Line 6011  sub _tree_construction_main ($) {
6011                my $i;                my $i;
6012                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6013                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6014                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_ROW_EL) {
6015                      !!!cp ('t228');
6016                    $i = $_;                    $i = $_;
6017                    last INSCOPE;                    last INSCOPE;
6018                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6019                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
6020                    last INSCOPE;                    last INSCOPE;
6021                  }                  }
6022                } # INSCOPE                } # INSCOPE
6023                unless (defined $i) {                unless (defined $i) {
6024                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
6025                    !!!parse-error (type => 'unmatched end tag',
6026                                    text => $token->{tag_name}, token => $token);
6027                  ## Ignore the token                  ## Ignore the token
6028                    !!!nack ('t230.1');
6029                  !!!next-token;                  !!!next-token;
6030                  redo B;                  next B;
6031                  } else {
6032                    !!!cp ('t232');
6033                }                }
6034    
6035                ## Clear back to table row context                ## Clear back to table row context
6036                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6037                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6038                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6039                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6040                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6041                }                }
6042    
6043                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6044                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6045                !!!next-token;                !!!next-token;
6046                redo B;                !!!nack ('t231.1');
6047                  next B;
6048              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6049                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6050                  ## As if </tr>                  ## As if </tr>
# Line 3946  sub _tree_construction_main ($) { Line 6052  sub _tree_construction_main ($) {
6052                  my $i;                  my $i;
6053                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6054                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6055                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6056                        !!!cp ('t233');
6057                      $i = $_;                      $i = $_;
6058                      last INSCOPE;                      last INSCOPE;
6059                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6060                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
6061                      last INSCOPE;                      last INSCOPE;
6062                    }                    }
6063                  } # INSCOPE                  } # INSCOPE
6064                  unless (defined $i) {                  unless (defined $i) {
6065                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
6066    ## TODO: The following is wrong.
6067                      !!!parse-error (type => 'unmatched end tag',
6068                                      text => $token->{type}, token => $token);
6069                    ## Ignore the token                    ## Ignore the token
6070                      !!!nack ('t236.1');
6071                    !!!next-token;                    !!!next-token;
6072                    redo B;                    next B;
6073                  }                  }
6074                                    
6075                  ## Clear back to table row context                  ## Clear back to table row context
6076                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6077                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6078                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
6079                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6080                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6081                  }                  }
6082                                    
# Line 3980  sub _tree_construction_main ($) { Line 6090  sub _tree_construction_main ($) {
6090                  my $i;                  my $i;
6091                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6092                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6093                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
6094                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
6095                      $i = $_;                      $i = $_;
6096                      last INSCOPE;                      last INSCOPE;
6097                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6098                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
6099                      last INSCOPE;                      last INSCOPE;
6100                    }                    }
6101                  } # INSCOPE                  } # INSCOPE
6102                  unless (defined $i) {                  unless (defined $i) {
6103                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
6104                      !!!parse-error (type => 'unmatched end tag',
6105                                      text => $token->{tag_name}, token => $token);
6106                    ## Ignore the token                    ## Ignore the token
6107                      !!!nack ('t239.1');
6108                    !!!next-token;                    !!!next-token;
6109                    redo B;                    next B;
6110                  }                  }
6111                                    
6112                  ## Clear back to table body context                  ## Clear back to table body context
6113                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6114                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
6115                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6116                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6117                  }                  }
6118                                    
# Line 4018  sub _tree_construction_main ($) { Line 6128  sub _tree_construction_main ($) {
6128                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6129                }                }
6130    
6131                  ## NOTE: </table> in the "in table" insertion mode.
6132                  ## When you edit the code fragment below, please ensure that
6133                  ## the code for <table> in the "in table" insertion mode
6134                  ## is synced with it.
6135    
6136                ## have a table element in table scope                ## have a table element in table scope
6137                my $i;                my $i;
6138                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6139                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6140                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_EL) {
6141                      !!!cp ('t241');
6142                    $i = $_;                    $i = $_;
6143                    last INSCOPE;                    last INSCOPE;
6144                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6145                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6146                    last INSCOPE;                    last INSCOPE;
6147                  }                  }
6148                } # INSCOPE                } # INSCOPE
6149                unless (defined $i) {                unless (defined $i) {
6150                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6151                    !!!parse-error (type => 'unmatched end tag',
6152                                    text => $token->{tag_name}, token => $token);
6153                  ## Ignore the token                  ## Ignore the token
6154                    !!!nack ('t243.1');
6155                  !!!next-token;                  !!!next-token;
6156                  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]);  
6157                }                }
6158                                    
6159                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6160                  pop @{$open_tables};
6161                                
6162                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6163                                
6164                !!!next-token;                !!!next-token;
6165                redo B;                next B;
6166              } elsif ({              } elsif ({
6167                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6168                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 4069  sub _tree_construction_main ($) { Line 6172  sub _tree_construction_main ($) {
6172                  my $i;                  my $i;
6173                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6174                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6175                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6176                        !!!cp ('t247');
6177                      $i = $_;                      $i = $_;
6178                      last INSCOPE;                      last INSCOPE;
6179                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6180                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
6181                      last INSCOPE;                      last INSCOPE;
6182                    }                    }
6183                  } # INSCOPE                  } # INSCOPE
6184                    unless (defined $i) {                    unless (defined $i) {
6185                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
6186                        !!!parse-error (type => 'unmatched end tag',
6187                                        text => $token->{tag_name}, token => $token);
6188                      ## Ignore the token                      ## Ignore the token
6189                        !!!nack ('t249.1');
6190                      !!!next-token;                      !!!next-token;
6191                      redo B;                      next B;
6192                    }                    }
6193                                    
6194                  ## As if </tr>                  ## As if </tr>
# Line 4090  sub _tree_construction_main ($) { Line 6196  sub _tree_construction_main ($) {
6196                  my $i;                  my $i;
6197                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6198                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6199                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6200                        !!!cp ('t250');
6201                      $i = $_;                      $i = $_;
6202                      last INSCOPE;                      last INSCOPE;
6203                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6204                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
6205                      last INSCOPE;                      last INSCOPE;
6206                    }                    }
6207                  } # INSCOPE                  } # INSCOPE
6208                    unless (defined $i) {                    unless (defined $i) {
6209                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
6210                        !!!parse-error (type => 'unmatched end tag',
6211                                        text => 'tr', token => $token);
6212                      ## Ignore the token                      ## Ignore the token
6213                        !!!nack ('t252.1');
6214                      !!!next-token;                      !!!next-token;
6215                      redo B;                      next B;
6216                    }                    }
6217                                    
6218                  ## Clear back to table row context                  ## Clear back to table row context
6219                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6220                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6221                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
6222                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6223                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6224                  }                  }
6225                                    
# Line 4123  sub _tree_construction_main ($) { Line 6232  sub _tree_construction_main ($) {
6232                my $i;                my $i;
6233                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6234                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6235                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6236                      !!!cp ('t254');
6237                    $i = $_;                    $i = $_;
6238                    last INSCOPE;                    last INSCOPE;
6239                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6240                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6241                    last INSCOPE;                    last INSCOPE;
6242                  }                  }
6243                } # INSCOPE                } # INSCOPE
6244                unless (defined $i) {                unless (defined $i) {
6245                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
6246                    !!!parse-error (type => 'unmatched end tag',
6247                                    text => $token->{tag_name}, token => $token);
6248                  ## Ignore the token                  ## Ignore the token
6249                    !!!nack ('t256.1');
6250                  !!!next-token;                  !!!next-token;
6251                  redo B;                  next B;
6252                }                }
6253    
6254                ## Clear back to table body context                ## Clear back to table body context
6255                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6256                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6257                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6258                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6259                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6260                }                }
6261    
6262                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6263                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6264                  !!!nack ('t257.1');
6265                !!!next-token;                !!!next-token;
6266                redo B;                next B;
6267              } elsif ({              } elsif ({
6268                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6269                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6270                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6271                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6272                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6273                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6274                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6275                !!!next-token;                            text => $token->{tag_name}, token => $token);
6276                redo B;            ## Ignore the token
6277          } else {            !!!nack ('t258.1');
6278            !!!parse-error (type => 'in table:/'.$token->{tag_name});             !!!next-token;
6279              next B;
6280            } else {
6281              !!!cp ('t259');
6282              !!!parse-error (type => 'in table:/',
6283                              text => $token->{tag_name}, token => $token);
6284    
6285            $insert = $insert_to_foster;            $insert = $insert_to_foster;
6286            #            #
6287          }          }
6288          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6289            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6290                    @{$self->{open_elements}} == 1) { # redundant, maybe
6291              !!!parse-error (type => 'in body:#eof', token => $token);
6292              !!!cp ('t259.1');
6293              #
6294            } else {
6295              !!!cp ('t259.2');
6296              #
6297            }
6298    
6299            ## Stop parsing
6300            last B;
6301        } else {        } else {
6302          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6303        }        }
6304      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6305            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6306              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6307                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6308                unless (length $token->{data}) {                unless (length $token->{data}) {
6309                    !!!cp ('t260');
6310                  !!!next-token;                  !!!next-token;
6311                  redo B;                  next B;
6312                }                }
6313              }              }
6314                            
6315                !!!cp ('t261');
6316              #              #
6317            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6318              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6319                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
6320                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6321                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6322                  !!!ack ('t262.1');
6323                !!!next-token;                !!!next-token;
6324                redo B;                next B;
6325              } else {              } else {
6326                  !!!cp ('t263');
6327                #                #
6328              }              }
6329            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6330              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6331                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6332                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
6333                    !!!parse-error (type => 'unmatched end tag',
6334                                    text => 'colgroup', token => $token);
6335                  ## Ignore the token                  ## Ignore the token
6336                  !!!next-token;                  !!!next-token;
6337                  redo B;                  next B;
6338                } else {                } else {
6339                    !!!cp ('t265');
6340                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6341                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6342                  !!!next-token;                  !!!next-token;
6343                  redo B;                              next B;            
6344                }                }
6345              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6346                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
6347                  !!!parse-error (type => 'unmatched end tag',
6348                                  text => 'col', token => $token);
6349                ## Ignore the token                ## Ignore the token
6350                !!!next-token;                !!!next-token;
6351                redo B;                next B;
6352              } else {              } else {
6353                  !!!cp ('t267');
6354                #                #
6355              }              }
6356            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6357              #          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6358            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6359              !!!cp ('t270.2');
6360              ## Stop parsing.
6361              last B;
6362            } else {
6363              ## NOTE: As if </colgroup>.
6364              !!!cp ('t270.1');
6365              pop @{$self->{open_elements}}; # colgroup
6366              $self->{insertion_mode} = IN_TABLE_IM;
6367              ## Reprocess.
6368              next B;
6369            }
6370          } else {
6371            die "$0: $token->{type}: Unknown token type";
6372          }
6373    
6374            ## As if </colgroup>            ## As if </colgroup>
6375            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6376              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
6377    ## TODO: Wrong error type?
6378                !!!parse-error (type => 'unmatched end tag',
6379                                text => 'colgroup', token => $token);
6380              ## Ignore the token              ## Ignore the token
6381                !!!nack ('t269.1');
6382              !!!next-token;              !!!next-token;
6383              redo B;              next B;
6384            } else {            } else {
6385                !!!cp ('t270');
6386              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6387              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6388                !!!ack-later;
6389              ## reprocess              ## reprocess
6390              redo B;              next B;
6391            }            }
6392      } elsif ($self->{insertion_mode} == IN_SELECT_IM) {      } elsif ($self->{insertion_mode} & SELECT_IMS) {
6393        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6394            !!!cp ('t271');
6395          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6396          !!!next-token;          !!!next-token;
6397          redo B;          next B;
6398        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6399              if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
6400                if ($self->{open_elements}->[-1]->[1] eq 'option') {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6401                  ## As if </option>              !!!cp ('t272');
6402                  pop @{$self->{open_elements}};              ## As if </option>
6403                }              pop @{$self->{open_elements}};
6404              } else {
6405                !!!cp ('t273');
6406              }
6407    
6408                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6409                !!!next-token;            !!!nack ('t273.1');
6410                redo B;            !!!next-token;
6411              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6412                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6413                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6414                  pop @{$self->{open_elements}};              !!!cp ('t274');
6415                }              ## As if </option>
6416                pop @{$self->{open_elements}};
6417              } else {
6418                !!!cp ('t275');
6419              }
6420    
6421                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6422                  ## As if </optgroup>              !!!cp ('t276');
6423                  pop @{$self->{open_elements}};              ## As if </optgroup>
6424                }              pop @{$self->{open_elements}};
6425              } else {
6426                !!!cp ('t277');
6427              }
6428    
6429                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6430                !!!next-token;            !!!nack ('t277.1');
6431                redo B;            !!!next-token;
6432              } elsif ($token->{tag_name} eq 'select') {            next B;
6433                !!!parse-error (type => 'not closed:select');          } elsif ({
6434                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6435                ## have an element in table scope                   }->{$token->{tag_name}} or
6436                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6437                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6438                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6439                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6440                    $i = $_;                     tr => 1, td => 1, th => 1,
6441                    last INSCOPE;                    }->{$token->{tag_name}})) {
6442                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6443                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6444                           }->{$node->[1]}) {                            token => $token);
6445                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6446                  }            ## as if there were </select> (otherwise).
6447                } # INSCOPE            ## have an element in table scope
6448                unless (defined $i) {            my $i;
6449                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6450                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6451                  !!!next-token;              if ($node->[1] == SELECT_EL) {
6452                  redo B;                !!!cp ('t278');
6453                }                $i = $_;
6454                  last INSCOPE;
6455                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6456                  !!!cp ('t279');
6457                  last INSCOPE;
6458                }
6459              } # INSCOPE
6460              unless (defined $i) {
6461                !!!cp ('t280');
6462                !!!parse-error (type => 'unmatched end tag',
6463                                text => 'select', token => $token);
6464                ## Ignore the token
6465                !!!nack ('t280.1');
6466                !!!next-token;
6467                next B;
6468              }
6469                                
6470                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6471              splice @{$self->{open_elements}}, $i;
6472    
6473                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6474    
6475                !!!next-token;            if ($token->{tag_name} eq 'select') {
6476                redo B;              !!!nack ('t281.2');
6477                !!!next-token;
6478                next B;
6479              } else {
6480                !!!cp ('t281.1');
6481                !!!ack-later;
6482                ## Reprocess the token.
6483                next B;
6484              }
6485          } else {          } else {
6486            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!cp ('t282');
6487              !!!parse-error (type => 'in select',
6488                              text => $token->{tag_name}, token => $token);
6489            ## Ignore the token            ## Ignore the token
6490              !!!nack ('t282.1');
6491            !!!next-token;            !!!next-token;
6492            redo B;            next B;
6493          }          }
6494        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6495              if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
6496                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6497                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6498                  ## As if </option>              !!!cp ('t283');
6499                  splice @{$self->{open_elements}}, -2;              ## As if </option>
6500                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              splice @{$self->{open_elements}}, -2;
6501                  pop @{$self->{open_elements}};            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6502                } else {              !!!cp ('t284');
6503                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              pop @{$self->{open_elements}};
6504                  ## Ignore the token            } else {
6505                }              !!!cp ('t285');
6506                !!!next-token;              !!!parse-error (type => 'unmatched end tag',
6507                redo B;                              text => $token->{tag_name}, token => $token);
6508              } elsif ($token->{tag_name} eq 'option') {              ## Ignore the token
6509                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6510                  pop @{$self->{open_elements}};            !!!nack ('t285.1');
6511                } else {            !!!next-token;
6512                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            next B;
6513                  ## Ignore the token          } elsif ($token->{tag_name} eq 'option') {
6514                }            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6515                !!!next-token;              !!!cp ('t286');
6516                redo B;              pop @{$self->{open_elements}};
6517              } elsif ($token->{tag_name} eq 'select') {            } else {
6518                ## have an element in table scope              !!!cp ('t287');
6519                my $i;              !!!parse-error (type => 'unmatched end tag',
6520                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                              text => $token->{tag_name}, token => $token);
6521                  my $node = $self->{open_elements}->[$_];              ## Ignore the token
6522                  if ($node->[1] eq $token->{tag_name}) {            }
6523                    $i = $_;            !!!nack ('t287.1');
6524                    last INSCOPE;            !!!next-token;
6525                  } elsif ({            next B;
6526                            table => 1, html => 1,          } elsif ($token->{tag_name} eq 'select') {
6527                           }->{$node->[1]}) {            ## have an element in table scope
6528                    last INSCOPE;            my $i;
6529                  }            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6530                } # INSCOPE              my $node = $self->{open_elements}->[$_];
6531                unless (defined $i) {              if ($node->[1] == SELECT_EL) {
6532                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t288');
6533                  ## Ignore the token                $i = $_;
6534                  !!!next-token;                last INSCOPE;
6535                  redo B;              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6536                }                !!!cp ('t289');
6537                  last INSCOPE;
6538                }
6539              } # INSCOPE
6540              unless (defined $i) {
6541                !!!cp ('t290');
6542                !!!parse-error (type => 'unmatched end tag',
6543                                text => $token->{tag_name}, token => $token);
6544                ## Ignore the token
6545                !!!nack ('t290.1');
6546                !!!next-token;
6547                next B;
6548              }
6549                                
6550                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6551              splice @{$self->{open_elements}}, $i;
6552    
6553                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6554    
6555                !!!next-token;            !!!nack ('t291.1');
6556                redo B;            !!!next-token;
6557              } elsif ({            next B;
6558                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6559                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6560                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6561                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6562                     }->{$token->{tag_name}}) {
6563    ## TODO: The following is wrong?
6564              !!!parse-error (type => 'unmatched end tag',
6565                              text => $token->{tag_name}, token => $token);
6566                                
6567                ## have an element in table scope            ## have an element in table scope
6568                my $i;            my $i;
6569                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6570                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6571                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6572                    $i = $_;                !!!cp ('t292');
6573                    last INSCOPE;                $i = $_;
6574                  } elsif ({                last INSCOPE;
6575                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6576                           }->{$node->[1]}) {                !!!cp ('t293');
6577                    last INSCOPE;                last INSCOPE;
6578                  }              }
6579                } # INSCOPE            } # INSCOPE
6580                unless (defined $i) {            unless (defined $i) {
6581                  ## Ignore the token              !!!cp ('t294');
6582                  !!!next-token;              ## Ignore the token
6583                  redo B;              !!!nack ('t294.1');
6584                }              !!!next-token;
6585                next B;
6586              }
6587                                
6588                ## As if </select>            ## As if </select>
6589                ## have an element in table scope            ## have an element in table scope
6590                undef $i;            undef $i;
6591                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6592                  my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
6593                  if ($node->[1] eq 'select') {              if ($node->[1] == SELECT_EL) {
6594                    $i = $_;                !!!cp ('t295');
6595                    last INSCOPE;                $i = $_;
6596                  } elsif ({                last INSCOPE;
6597                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6598                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
6599                    last INSCOPE;                !!!cp ('t296');
6600                  }                last INSCOPE;
6601                } # INSCOPE              }
6602                unless (defined $i) {            } # INSCOPE
6603                  !!!parse-error (type => 'unmatched end tag:select');            unless (defined $i) {
6604                  ## Ignore the </select> token              !!!cp ('t297');
6605                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
6606                  redo B;              !!!parse-error (type => 'unmatched end tag',
6607                }                              text => 'select', token => $token);
6608                ## Ignore the </select> token
6609                !!!nack ('t297.1');
6610                !!!next-token; ## TODO: ok?
6611                next B;
6612              }
6613                                
6614                splice @{$self->{open_elements}}, $i;            !!!cp ('t298');
6615              splice @{$self->{open_elements}}, $i;
6616    
6617                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6618    
6619                ## reprocess            !!!ack-later;
6620                redo B;            ## reprocess
6621              next B;
6622          } else {          } else {
6623            !!!parse-error (type => 'in select:/'.$token->{tag_name});            !!!cp ('t299');
6624              !!!parse-error (type => 'in select:/',
6625                              text => $token->{tag_name}, token => $token);
6626            ## Ignore the token            ## Ignore the token
6627              !!!nack ('t299.3');
6628            !!!next-token;            !!!next-token;
6629            redo B;            next B;
6630          }          }
6631          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6632            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6633                    @{$self->{open_elements}} == 1) { # redundant, maybe
6634              !!!cp ('t299.1');
6635              !!!parse-error (type => 'in body:#eof', token => $token);
6636            } else {
6637              !!!cp ('t299.2');
6638            }
6639    
6640            ## Stop parsing.
6641            last B;
6642        } else {        } else {
6643          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6644        }        }
6645      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6646        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6647          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6648            my $data = $1;            my $data = $1;
6649            ## As if in body            ## As if in body
6650            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 4412  sub _tree_construction_main ($) { Line 6652  sub _tree_construction_main ($) {
6652            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6653                        
6654            unless (length $token->{data}) {            unless (length $token->{data}) {
6655                !!!cp ('t300');
6656              !!!next-token;              !!!next-token;
6657              redo B;              next B;
6658            }            }
6659          }          }
6660                    
6661          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6662            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
6663              !!!parse-error (type => 'after html:#text', token => $token);
6664            ## Reprocess in the "main" phase, "after body" insertion mode...            #
6665            } else {
6666              !!!cp ('t302');
6667              ## "after body" insertion mode
6668              !!!parse-error (type => 'after body:#text', token => $token);
6669              #
6670          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6671    
6672          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6673          ## reprocess          ## reprocess
6674          redo B;          next B;
6675        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6676          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6677            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
6678                        !!!parse-error (type => 'after html',
6679            ## Reprocess in the "main" phase, "after body" insertion mode...                            text => $token->{tag_name}, token => $token);
6680              #
6681            } else {
6682              !!!cp ('t304');
6683              ## "after body" insertion mode
6684              !!!parse-error (type => 'after body',
6685                              text => $token->{tag_name}, token => $token);
6686              #
6687          }          }
6688    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6689          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6690            !!!ack-later;
6691          ## reprocess          ## reprocess
6692          redo B;          next B;
6693        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6694          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6695            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
6696              !!!parse-error (type => 'after html:/',
6697                              text => $token->{tag_name}, token => $token);
6698                        
6699            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6700            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess.
6701              next B;
6702            } else {
6703              !!!cp ('t306');
6704          }          }
6705    
6706          ## "after body" insertion mode          ## "after body" insertion mode
6707          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6708            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6709              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
6710                !!!parse-error (type => 'unmatched end tag',
6711                                text => 'html', token => $token);
6712              ## Ignore the token              ## Ignore the token
6713              !!!next-token;              !!!next-token;
6714              redo B;              next B;
6715            } else {            } else {
6716                !!!cp ('t308');
6717              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6718              !!!next-token;              !!!next-token;
6719              redo B;              next B;
6720            }            }
6721          } else {          } else {
6722            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
6723              !!!parse-error (type => 'after body:/',
6724                              text => $token->{tag_name}, token => $token);
6725    
6726            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6727            ## reprocess            ## reprocess
6728            redo B;            next B;
6729          }          }
6730          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6731            !!!cp ('t309.2');
6732            ## Stop parsing
6733            last B;
6734        } else {        } else {
6735          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6736        }        }
6737      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6738        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6739          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6740            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6741                        
6742            unless (length $token->{data}) {            unless (length $token->{data}) {
6743                !!!cp ('t310');
6744              !!!next-token;              !!!next-token;
6745              redo B;              next B;
6746            }            }
6747          }          }
6748                    
6749          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6750            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6751              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
6752                !!!parse-error (type => 'in frameset:#text', token => $token);
6753            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6754              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
6755            } else { # "after html frameset"              !!!parse-error (type => 'after frameset:#text', token => $token);
6756              !!!parse-error (type => 'after html:#character');            } else { # "after after frameset"
6757                !!!cp ('t313');
6758              $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');  
6759            }            }
6760                        
6761            ## Ignore the token.            ## Ignore the token.
6762            if (length $token->{data}) {            if (length $token->{data}) {
6763                !!!cp ('t314');
6764              ## reprocess the rest of characters              ## reprocess the rest of characters
6765            } else {            } else {
6766                !!!cp ('t315');
6767              !!!next-token;              !!!next-token;
6768            }            }
6769            redo B;            next B;
6770          }          }
6771                    
6772          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6773        } 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...  
         }  
   
6774          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6775              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6776            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6777              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6778              !!!nack ('t318.1');
6779            !!!next-token;            !!!next-token;
6780            redo B;            next B;
6781          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6782                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6783            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
6784              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6785            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6786              !!!ack ('t319.1');
6787            !!!next-token;            !!!next-token;
6788            redo B;            next B;
6789          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6790            ## NOTE: As if in body.            !!!cp ('t320');
6791            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6792            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6793              next B;
6794    
6795              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6796              ## has no parse error.
6797          } else {          } else {
6798            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6799              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
6800            } else {              !!!parse-error (type => 'in frameset',
6801              !!!parse-error (type => 'after frameset:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6802              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6803                !!!cp ('t322');
6804                !!!parse-error (type => 'after frameset',
6805                                text => $token->{tag_name}, token => $token);
6806              } else { # "after after frameset"
6807                !!!cp ('t322.2');
6808                !!!parse-error (type => 'after after frameset',
6809                                text => $token->{tag_name}, token => $token);
6810            }            }
6811            ## Ignore the token            ## Ignore the token
6812              !!!nack ('t322.1');
6813            !!!next-token;            !!!next-token;
6814            redo B;            next B;
6815          }          }
6816        } 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...  
         }  
   
6817          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6818              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6819            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6820                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6821              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6822                !!!parse-error (type => 'unmatched end tag',
6823                                text => $token->{tag_name}, token => $token);
6824              ## Ignore the token              ## Ignore the token
6825              !!!next-token;              !!!next-token;
6826            } else {            } else {
6827                !!!cp ('t326');
6828              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6829              !!!next-token;              !!!next-token;
6830            }            }
6831    
6832            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6833                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6834                !!!cp ('t327');
6835              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6836              } else {
6837                !!!cp ('t328');
6838            }            }
6839            redo B;            next B;
6840          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6841                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6842              !!!cp ('t329');
6843            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6844            !!!next-token;            !!!next-token;
6845            redo B;            next B;
6846          } else {          } else {
6847            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6848              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
6849            } else {              !!!parse-error (type => 'in frameset:/',
6850              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6851              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6852                !!!cp ('t330.1');
6853                !!!parse-error (type => 'after frameset:/',
6854                                text => $token->{tag_name}, token => $token);
6855              } else { # "after after html"
6856                !!!cp ('t331');
6857                !!!parse-error (type => 'after after frameset:/',
6858                                text => $token->{tag_name}, token => $token);
6859            }            }
6860            ## Ignore the token            ## Ignore the token
6861            !!!next-token;            !!!next-token;
6862            redo B;            next B;
6863            }
6864          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6865            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6866                    @{$self->{open_elements}} == 1) { # redundant, maybe
6867              !!!cp ('t331.1');
6868              !!!parse-error (type => 'in body:#eof', token => $token);
6869            } else {
6870              !!!cp ('t331.2');
6871          }          }
6872            
6873            ## Stop parsing
6874            last B;
6875        } else {        } else {
6876          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6877        }        }
   
       ## ISSUE: An issue in spec here  
6878      } else {      } else {
6879        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6880      }      }
# Line 4591  sub _tree_construction_main ($) { Line 6882  sub _tree_construction_main ($) {
6882      ## "in body" insertion mode      ## "in body" insertion mode
6883      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
6884        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6885            !!!cp ('t332');
6886          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6887          $script_start_tag->($insert);          $script_start_tag->();
6888          redo B;          next B;
6889        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6890            !!!cp ('t333');
6891          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6892          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6893          redo B;          next B;
6894        } elsif ({        } elsif ({
6895                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6896                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6897            !!!cp ('t334');
6898          ## 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
6899          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6900          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6901            !!!ack ('t334.1');
6902          !!!next-token;          !!!next-token;
6903          redo B;          next B;
6904        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6905          ## 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
6906          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6907          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6908    
6909          unless ($self->{confident}) {          unless ($self->{confident}) {
6910            if ($token->{attributes}->{charset}) { ## TODO: And if supported            if ($token->{attributes}->{charset}) {
6911                !!!cp ('t335');
6912                ## NOTE: Whether the encoding is supported or not is handled
6913                ## in the {change_encoding} callback.
6914              $self->{change_encoding}              $self->{change_encoding}
6915                  ->($self, $token->{attributes}->{charset}->{value});                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6916                            
6917              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6918                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6919                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6920                                           ->{has_reference});                                           ->{has_reference});
6921            } elsif ($token->{attributes}->{content}) {            } elsif ($token->{attributes}->{content}) {
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
6922              if ($token->{attributes}->{content}->{value}              if ($token->{attributes}->{content}->{value}
6923                  =~ /\A[^;]*;[\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6924                      [\x09-\x0D\x20]*=                      [\x09\x0A\x0C\x0D\x20]*=
6925                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6926                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                      ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6927                       /x) {
6928                  !!!cp ('t336');
6929                  ## NOTE: Whether the encoding is supported or not is handled
6930                  ## in the {change_encoding} callback.
6931                $self->{change_encoding}                $self->{change_encoding}
6932                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3);                    ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6933                $meta_el->[0]->get_attribute_node_ns (undef, 'content')                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6934                    ->set_user_data (manakai_has_reference =>                    ->set_user_data (manakai_has_reference =>
6935                                         $token->{attributes}->{content}                                         $token->{attributes}->{content}
# Line 4637  sub _tree_construction_main ($) { Line 6938  sub _tree_construction_main ($) {
6938            }            }
6939          } else {          } else {
6940            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
6941                !!!cp ('t337');
6942              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6943                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6944                                       $token->{attributes}->{charset}                                       $token->{attributes}->{charset}
6945                                           ->{has_reference});                                           ->{has_reference});
6946            }            }
6947            if ($token->{attributes}->{content}) {            if ($token->{attributes}->{content}) {
6948                !!!cp ('t338');
6949              $meta_el->[0]->get_attribute_node_ns (undef, 'content')              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6950                  ->set_user_data (manakai_has_reference =>                  ->set_user_data (manakai_has_reference =>
6951                                       $token->{attributes}->{content}                                       $token->{attributes}->{content}
# Line 4650  sub _tree_construction_main ($) { Line 6953  sub _tree_construction_main ($) {
6953            }            }
6954          }          }
6955    
6956            !!!ack ('t338.1');
6957          !!!next-token;          !!!next-token;
6958          redo B;          next B;
6959        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6960          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
6961          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6962          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6963            if (defined $self->{head_element}) {          next B;
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6964        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6965          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6966                                
6967          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6968              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6969              !!!cp ('t342');
6970            ## Ignore the token            ## Ignore the token
6971          } else {          } else {
6972            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
6973            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
6974              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6975                  !!!cp ('t343');
6976                $body_el->set_attribute_ns                $body_el->set_attribute_ns
6977                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
6978                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
6979              }              }
6980            }            }
6981          }          }
6982            !!!nack ('t343.1');
6983          !!!next-token;          !!!next-token;
6984          redo B;          next B;
6985        } elsif ({        } elsif ({
6986                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6987                  div => 1, dl => 1, fieldset => 1, listing => 1,  
6988                  menu => 1, ol => 1, p => 1, ul => 1,                  ## NOTE: The normal one
6989                  pre => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6990                    center => 1, datagrid => 1, details => 1, dialog => 1,
6991                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6992                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6993                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6994                    section => 1, ul => 1,
6995                    ## NOTE: As normal, but drops leading newline
6996                    pre => 1, listing => 1,
6997                    ## NOTE: As normal, but interacts with the form element pointer
6998                    form => 1,
6999                    
7000                    table => 1,
7001                    hr => 1,
7002                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7003            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
7004              !!!cp ('t350');
7005              !!!parse-error (type => 'in form:form', token => $token);
7006              ## Ignore the token
7007              !!!nack ('t350.1');
7008              !!!next-token;
7009              next B;
7010            }
7011    
7012          ## has a p element in scope          ## has a p element in scope
7013          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7014            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7015              !!!back-token;              !!!cp ('t344');
7016              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <form>
7017              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7018            } elsif ({                        line => $token->{line}, column => $token->{column}};
7019                      table => 1, caption => 1, td => 1, th => 1,              next B;
7020                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7021                     }->{$_->[1]}) {              !!!cp ('t345');
7022              last INSCOPE;              last INSCOPE;
7023            }            }
7024          } # INSCOPE          } # INSCOPE
7025                        
7026          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7027          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7028              !!!nack ('t346.1');
7029            !!!next-token;            !!!next-token;
7030            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7031              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
7032              unless (length $token->{data}) {              unless (length $token->{data}) {
7033                  !!!cp ('t346');
7034                !!!next-token;                !!!next-token;
7035                } else {
7036                  !!!cp ('t349');
7037              }              }
7038              } else {
7039                !!!cp ('t348');
7040            }            }
7041          } else {          } elsif ($token->{tag_name} eq 'form') {
7042              !!!cp ('t347.1');
7043              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7044    
7045              !!!nack ('t347.2');
7046            !!!next-token;            !!!next-token;
7047          }          } elsif ($token->{tag_name} eq 'table') {
7048          redo B;            !!!cp ('t382');
7049        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7050          if (defined $self->{form_element}) {            
7051            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
7052            ## Ignore the token  
7053              !!!nack ('t382.1');
7054              !!!next-token;
7055            } elsif ($token->{tag_name} eq 'hr') {
7056              !!!cp ('t386');
7057              pop @{$self->{open_elements}};
7058            
7059              !!!nack ('t386.1');
7060            !!!next-token;            !!!next-token;
           redo B;  
7061          } else {          } else {
7062            ## 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];  
7063            !!!next-token;            !!!next-token;
           redo B;  
7064          }          }
7065            next B;
7066        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7067          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7068          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7069            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7070              !!!back-token;            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7071              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7072              redo B;            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7073            } elsif ({            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7074                      table => 1, caption => 1, td => 1, th => 1,            ## object (Fx)
7075                      button => 1, marquee => 1, object => 1, html => 1,            ## Generate non-tree (non-conforming)
7076                     }->{$_->[1]}) {            ## basefont (IE7 (where basefont is non-void)), center (IE),
7077              last INSCOPE;            ## form (IE), hn (IE)
7078            }          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7079          } # INSCOPE            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7080                        ## div (Fx, S)
7081          ## Step 1  
7082            my $non_optional;
7083          my $i = -1;          my $i = -1;
7084          my $node = $self->{open_elements}->[$i];  
7085          LI: {          ## 1.
7086            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7087            if ($node->[1] eq 'li') {            if ($node->[1] == LI_EL) {
7088              if ($i != -1) {              ## 2. (a) As if </li>
7089                !!!parse-error (type => 'end tag missing:'.              {
7090                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7091                  #
7092    
7093                  ## Otherwise
7094    
7095                  ## 1. generate implied end tags, except for </li>
7096                  #
7097    
7098                  ## 2. If current node != "li", parse error
7099                  if ($non_optional) {
7100                    !!!parse-error (type => 'not closed',
7101                                    text => $non_optional->[0]->manakai_local_name,
7102                                    token => $token);
7103                    !!!cp ('t355');
7104                  } else {
7105                    !!!cp ('t356');
7106                  }
7107    
7108                  ## 3. Pop
7109                  splice @{$self->{open_elements}}, $i;
7110              }              }
7111              splice @{$self->{open_elements}}, $i;  
7112              last LI;              last; ## 2. (b) goto 5.
7113            }            } elsif (
7114                                 ## NOTE: not "formatting" and not "phrasing"
7115            ## Step 3                     ($node->[1] & SPECIAL_EL or
7116            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7117                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7118                ($special_category->{$node->[1]} or                     (not $node->[1] & ADDRESS_DIV_P_EL)
7119                 $scoping_category->{$node->[1]}) and                    ) {
7120                $node->[1] ne 'address' and $node->[1] ne 'div') {              ## 3.
7121              last LI;              !!!cp ('t357');
7122                last; ## goto 5.
7123              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7124                !!!cp ('t358');
7125                #
7126              } else {
7127                !!!cp ('t359');
7128                $non_optional ||= $node;
7129                #
7130            }            }
7131                        ## 4.
7132            ## Step 4            ## goto 2.
7133            $i--;            $i--;
7134            $node = $self->{open_elements}->[$i];          }
7135            redo LI;  
7136          } # 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  
7137          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7138            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7139              !!!back-token;              !!!cp ('t353');
7140              $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
7141              redo B;              ## NOTE: |<p><li>|, for example.
7142            } elsif ({  
7143                      table => 1, caption => 1, td => 1, th => 1,              !!!back-token; # <x>
7144                      button => 1, marquee => 1, object => 1, html => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7145                     }->{$_->[1]}) {                        line => $token->{line}, column => $token->{column}};
7146                next B;
7147              } elsif ($_->[1] & SCOPING_EL) {
7148                !!!cp ('t354');
7149              last INSCOPE;              last INSCOPE;
7150            }            }
7151          } # INSCOPE          } # INSCOPE
7152              
7153          ## Step 1          ## 5. (b) insert
7154            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7155            !!!nack ('t359.1');
7156            !!!next-token;
7157            next B;
7158          } elsif ($token->{tag_name} eq 'dt' or
7159                   $token->{tag_name} eq 'dd') {
7160            ## NOTE: As normal, but imply </dt> or </dd> when ...
7161    
7162            my $non_optional;
7163          my $i = -1;          my $i = -1;
7164          my $node = $self->{open_elements}->[$i];  
7165          LI: {          ## 1.
7166            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7167            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] == DT_EL or $node->[1] == DD_EL) {
7168              if ($i != -1) {              ## 2. (a) As if </li>
7169                !!!parse-error (type => 'end tag missing:'.              {
7170                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7171                  #
7172    
7173                  ## Otherwise
7174    
7175                  ## 1. generate implied end tags, except for </dt> or </dd>
7176                  #
7177    
7178                  ## 2. If current node != "dt"|"dd", parse error
7179                  if ($non_optional) {
7180                    !!!parse-error (type => 'not closed',
7181                                    text => $non_optional->[0]->manakai_local_name,
7182                                    token => $token);
7183                    !!!cp ('t355.1');
7184                  } else {
7185                    !!!cp ('t356.1');
7186                  }
7187    
7188                  ## 3. Pop
7189                  splice @{$self->{open_elements}}, $i;
7190              }              }
7191              splice @{$self->{open_elements}}, $i;  
7192              last LI;              last; ## 2. (b) goto 5.
7193            }            } elsif (
7194                                 ## NOTE: not "formatting" and not "phrasing"
7195            ## Step 3                     ($node->[1] & SPECIAL_EL or
7196            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7197                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7198                ($special_category->{$node->[1]} or  
7199                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_DIV_P_EL)
7200                $node->[1] ne 'address' and $node->[1] ne 'div') {                    ) {
7201              last LI;              ## 3.
7202                !!!cp ('t357.1');
7203                last; ## goto 5.
7204              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7205                !!!cp ('t358.1');
7206                #
7207              } else {
7208                !!!cp ('t359.1');
7209                $non_optional ||= $node;
7210                #
7211            }            }
7212                        ## 4.
7213            ## Step 4            ## goto 2.
7214            $i--;            $i--;
7215            $node = $self->{open_elements}->[$i];          }
7216            redo LI;  
7217          } # 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  
7218          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7219            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7220              !!!back-token;              !!!cp ('t353.1');
7221              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <x>
7222              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7223            } elsif ({                        line => $token->{line}, column => $token->{column}};
7224                      table => 1, caption => 1, td => 1, th => 1,              next B;
7225                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7226                     }->{$_->[1]}) {              !!!cp ('t354.1');
7227              last INSCOPE;              last INSCOPE;
7228            }            }
7229          } # INSCOPE          } # INSCOPE
7230              
7231          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          ## 5. (b) insert
7232                      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7233          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          !!!nack ('t359.2');
             
7234          !!!next-token;          !!!next-token;
7235          redo B;          next B;
7236        } elsif ({        } elsif ($token->{tag_name} eq 'plaintext') {
7237                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          ## NOTE: As normal, but effectively ends parsing
7238                 }->{$token->{tag_name}}) {  
7239          ## has a p element in scope          ## has a p element in scope
7240          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7241            my $node = $self->{open_elements}->[$_];            if ($_->[1] == P_EL) {
7242            if ($node->[1] eq 'p') {              !!!cp ('t367');
7243              !!!back-token;              !!!back-token; # <plaintext>
7244              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7245              redo B;                        line => $token->{line}, column => $token->{column}};
7246            } elsif ({              next B;
7247                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
7248                      button => 1, marquee => 1, object => 1, html => 1,              !!!cp ('t368');
                    }->{$node->[1]}) {  
7249              last INSCOPE;              last INSCOPE;
7250            }            }
7251          } # INSCOPE          } # INSCOPE
7252                        
7253          ## 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;  
         #}  
7254                        
7255          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7256                        
7257            !!!nack ('t368.1');
7258          !!!next-token;          !!!next-token;
7259          redo B;          next B;
7260        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7261          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7262            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7263            if ($node->[1] eq 'a') {            if ($node->[1] == A_EL) {
7264              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
7265                !!!parse-error (type => 'in a:a', token => $token);
7266                            
7267              !!!back-token;              !!!back-token; # <a>
7268              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7269              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7270                $formatting_end_tag->($token);
7271                            
7272              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7273                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7274                    !!!cp ('t372');
7275                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
7276                  last AFE2;                  last AFE2;
7277                }                }
7278              } # AFE2              } # AFE2
7279              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
7280                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7281                    !!!cp ('t373');
7282                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
7283                  last OE;                  last OE;
7284                }                }
7285              } # OE              } # OE
7286              last AFE;              last AFE;
7287            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
7288                !!!cp ('t374');
7289              last AFE;              last AFE;
7290            }            }
7291          } # AFE          } # AFE
7292                        
7293          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7294    
7295          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7296          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7297    
7298            !!!nack ('t374.1');
7299          !!!next-token;          !!!next-token;
7300          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;  
7301        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7302          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7303    
7304          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7305          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7306            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7307            if ($node->[1] eq 'nobr') {            if ($node->[1] == NOBR_EL) {
7308              !!!parse-error (type => 'in nobr:nobr');              !!!cp ('t376');
7309              !!!back-token;              !!!parse-error (type => 'in nobr:nobr', token => $token);
7310              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              !!!back-token; # <nobr>
7311              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7312            } elsif ({                        line => $token->{line}, column => $token->{column}};
7313                      table => 1, caption => 1, td => 1, th => 1,              next B;
7314                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7315                     }->{$node->[1]}) {              !!!cp ('t377');
7316              last INSCOPE;              last INSCOPE;
7317            }            }
7318          } # INSCOPE          } # INSCOPE
7319                    
7320          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7321          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7322                    
7323            !!!nack ('t377.1');
7324          !!!next-token;          !!!next-token;
7325          redo B;          next B;
7326        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7327          ## has a button element in scope          ## has a button element in scope
7328          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7329            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7330            if ($node->[1] eq 'button') {            if ($node->[1] == BUTTON_EL) {
7331              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
7332              !!!back-token;              !!!parse-error (type => 'in button:button', token => $token);
7333              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              !!!back-token; # <button>
7334              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7335            } elsif ({                        line => $token->{line}, column => $token->{column}};
7336                      table => 1, caption => 1, td => 1, th => 1,              next B;
7337                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7338                     }->{$node->[1]}) {              !!!cp ('t379');
7339              last INSCOPE;              last INSCOPE;
7340            }            }
7341          } # INSCOPE          } # INSCOPE
7342                        
7343          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7344                        
7345          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7346          push @$active_formatting_elements, ['#marker', ''];  
7347            ## TODO: associate with $self->{form_element} if defined
7348    
         !!!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});  
7349          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7350            
7351          !!!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;  
             
7352          !!!next-token;          !!!next-token;
7353          redo B;          next B;
7354        } elsif ({        } elsif ({
7355                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7356                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7357                  image => 1,                  noembed => 1,
7358                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7359                    noscript => 0, ## TODO: 1 if scripting is enabled
7360                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7361          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7362            !!!parse-error (type => 'image');            !!!cp ('t381');
7363            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
7364            } else {
7365              !!!cp ('t399');
7366          }          }
7367            ## NOTE: There is an "as if in body" code clone.
7368          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7369          $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;  
7370        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7371          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7372                    
7373          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7374              !!!cp ('t389');
7375            ## Ignore the token            ## Ignore the token
7376              !!!nack ('t389'); ## NOTE: Not acknowledged.
7377            !!!next-token;            !!!next-token;
7378            redo B;            next B;
7379          } else {          } else {
7380              !!!ack ('t391.1');
7381    
7382            my $at = $token->{attributes};            my $at = $token->{attributes};
7383            my $form_attrs;            my $form_attrs;
7384            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 5093  sub _tree_construction_main ($) { Line 7388  sub _tree_construction_main ($) {
7388            delete $at->{prompt};            delete $at->{prompt};
7389            my @tokens = (            my @tokens = (
7390                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7391                           attributes => $form_attrs},                           attributes => $form_attrs,
7392                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7393                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7394                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7395                            {type => START_TAG_TOKEN, tag_name => 'p',
7396                             line => $token->{line}, column => $token->{column}},
7397                            {type => START_TAG_TOKEN, tag_name => 'label',
7398                             line => $token->{line}, column => $token->{column}},
7399                         );                         );
7400            if ($prompt_attr) {            if ($prompt_attr) {
7401              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
7402                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7403                               #line => $token->{line}, column => $token->{column},
7404                              };
7405            } else {            } else {
7406                !!!cp ('t391');
7407              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7408                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7409                               #line => $token->{line}, column => $token->{column},
7410                              }; # SHOULD
7411              ## TODO: make this configurable              ## TODO: make this configurable
7412            }            }
7413            push @tokens,            push @tokens,
7414                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7415                             line => $token->{line}, column => $token->{column}},
7416                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7417                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7418                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7419                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7420                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7421            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7422                             line => $token->{line}, column => $token->{column}},
7423                            {type => END_TAG_TOKEN, tag_name => 'form',
7424                             line => $token->{line}, column => $token->{column}};
7425            !!!back-token (@tokens);            !!!back-token (@tokens);
7426            redo B;            !!!next-token;
7427              next B;
7428          }          }
7429        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7430          my $tag_name = $token->{tag_name};          ## Step 1
7431          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
7432                    
7433            ## Step 2
7434          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7435    
7436            ## Step 3
7437            $self->{ignore_newline} = 1;
7438    
7439            ## Step 4
7440            ## ISSUE: This step is wrong. (r2302 enbugged)
7441    
7442            ## Step 5
7443          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7444          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7445            
7446          $insert->($el);          ## Step 6-7
7447                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7448          my $text = '';  
7449            !!!nack ('t392.1');
7450          !!!next-token;          !!!next-token;
7451          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7452            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7453            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7454              !!!next-token;          ## has an |option| element in scope
7455            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7456              my $node = $self->{open_elements}->[$_];
7457              if ($node->[1] == OPTION_EL) {
7458                !!!cp ('t397.1');
7459                ## NOTE: As if </option>
7460                !!!back-token; # <option> or <optgroup>
7461                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7462                          line => $token->{line}, column => $token->{column}};
7463                next B;
7464              } elsif ($node->[1] & SCOPING_EL) {
7465                !!!cp ('t397.2');
7466                last INSCOPE;
7467            }            }
7468          }          } # INSCOPE
7469          while ($token->{type} == CHARACTER_TOKEN) {  
7470            $text .= $token->{data};          $reconstruct_active_formatting_elements->($insert_to_current);
7471            !!!next-token;  
7472          }          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7473          if (length $text) {  
7474            $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});  
         }  
7475          !!!next-token;          !!!next-token;
7476          redo B;          redo B;
7477        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7478                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7479                  noembed => 1,          ## has a |ruby| element in scope
7480                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7481                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7482                 }->{$token->{tag_name}}) {            if ($node->[1] == RUBY_EL) {
7483          ## NOTE: There is an "as if in body" code clone.              !!!cp ('t398.1');
7484          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              ## generate implied end tags
7485                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7486                  !!!cp ('t398.2');
7487                  pop @{$self->{open_elements}};
7488                }
7489                unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7490                  !!!cp ('t398.3');
7491                  !!!parse-error (type => 'not closed',
7492                                  text => $self->{open_elements}->[-1]->[0]
7493                                      ->manakai_local_name,
7494                                  token => $token);
7495                  pop @{$self->{open_elements}}
7496                      while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
7497                }
7498                last INSCOPE;
7499              } elsif ($node->[1] & SCOPING_EL) {
7500                !!!cp ('t398.4');
7501                last INSCOPE;
7502              }
7503            } # INSCOPE
7504    
7505            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7506    
7507            !!!nack ('t398.5');
7508            !!!next-token;
7509          redo B;          redo B;
7510        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7511                   $token->{tag_name} eq 'svg') {
7512          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7513    
7514            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7515    
7516            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7517    
7518            ## "adjust foreign attributes" - done in insert-element-f
7519                    
7520          !!!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);
7521                    
7522          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7523              pop @{$self->{open_elements}};
7524              !!!ack ('t398.6');
7525            } else {
7526              !!!cp ('t398.7');
7527              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7528              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7529              ## mode, "in body" (not "in foreign content") secondary insertion
7530              ## mode, maybe.
7531            }
7532    
7533          !!!next-token;          !!!next-token;
7534          redo B;          next B;
7535        } elsif ({        } elsif ({
7536                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7537                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7538                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7539                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7540                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7541          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
7542            !!!parse-error (type => 'in body',
7543                            text => $token->{tag_name}, token => $token);
7544          ## Ignore the token          ## Ignore the token
7545            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7546            !!!next-token;
7547            next B;
7548          } elsif ($token->{tag_name} eq 'param' or
7549                   $token->{tag_name} eq 'source') {
7550            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7551            pop @{$self->{open_elements}};
7552    
7553            !!!ack ('t398.5');
7554          !!!next-token;          !!!next-token;
7555          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7556        } else {        } else {
7557            if ($token->{tag_name} eq 'image') {
7558              !!!cp ('t384');
7559              !!!parse-error (type => 'image', token => $token);
7560              $token->{tag_name} = 'img';
7561            } else {
7562              !!!cp ('t385');
7563            }
7564    
7565            ## NOTE: There is an "as if <br>" code clone.
7566          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7567                    
7568          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7569    
7570            if ({
7571                 applet => 1, marquee => 1, object => 1,
7572                }->{$token->{tag_name}}) {
7573              !!!cp ('t380');
7574              push @$active_formatting_elements, ['#marker', ''];
7575              !!!nack ('t380.1');
7576            } elsif ({
7577                      b => 1, big => 1, em => 1, font => 1, i => 1,
7578                      s => 1, small => 1, strike => 1,
7579                      strong => 1, tt => 1, u => 1,
7580                     }->{$token->{tag_name}}) {
7581              !!!cp ('t375');
7582              push @$active_formatting_elements, $self->{open_elements}->[-1];
7583              !!!nack ('t375.1');
7584            } elsif ($token->{tag_name} eq 'input') {
7585              !!!cp ('t388');
7586              ## TODO: associate with $self->{form_element} if defined
7587              pop @{$self->{open_elements}};
7588              !!!ack ('t388.2');
7589            } elsif ({
7590                      area => 1, basefont => 1, bgsound => 1, br => 1,
7591                      embed => 1, img => 1, spacer => 1, wbr => 1,
7592                     }->{$token->{tag_name}}) {
7593              !!!cp ('t388.1');
7594              pop @{$self->{open_elements}};
7595              !!!ack ('t388.3');
7596            } elsif ($token->{tag_name} eq 'select') {
7597              ## TODO: associate with $self->{form_element} if defined
7598            
7599              if ($self->{insertion_mode} & TABLE_IMS or
7600                  $self->{insertion_mode} & BODY_TABLE_IMS or
7601                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7602                !!!cp ('t400.1');
7603                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7604              } else {
7605                !!!cp ('t400.2');
7606                $self->{insertion_mode} = IN_SELECT_IM;
7607              }
7608              !!!nack ('t400.3');
7609            } else {
7610              !!!nack ('t402');
7611            }
7612                    
7613          !!!next-token;          !!!next-token;
7614          redo B;          next B;
7615        }        }
7616      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7617        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7618          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7619              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7620            for (@{$self->{open_elements}}) {          INSCOPE: {
7621              unless ({            for (reverse @{$self->{open_elements}}) {
7622                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] == BODY_EL) {
7623                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7624                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7625                      }->{$_->[1]}) {                last INSCOPE;
7626                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
7627                  !!!cp ('t405.1');
7628                  last;
7629              }              }
7630            }            }
7631    
7632            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7633            !!!next-token;  
7634            redo B;            !!!parse-error (type => 'unmatched end tag',
7635          } else {                            text => $token->{tag_name}, token => $token);
7636            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: Ignore the token.
           ## Ignore the token  
7637            !!!next-token;            !!!next-token;
7638            redo B;            next B;
7639            } # INSCOPE
7640    
7641            for (@{$self->{open_elements}}) {
7642              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7643                !!!cp ('t403');
7644                !!!parse-error (type => 'not closed',
7645                                text => $_->[0]->manakai_local_name,
7646                                token => $token);
7647                last;
7648              } else {
7649                !!!cp ('t404');
7650              }
7651          }          }
7652    
7653            $self->{insertion_mode} = AFTER_BODY_IM;
7654            !!!next-token;
7655            next B;
7656        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7657          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
7658            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7659            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7660              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              $self->{open_elements}->[1]->[1] == BODY_EL) {
7661              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7662                !!!cp ('t406');
7663                !!!parse-error (type => 'not closed',
7664                                text => $self->{open_elements}->[1]->[0]
7665                                    ->manakai_local_name,
7666                                token => $token);
7667              } else {
7668                !!!cp ('t407');
7669            }            }
7670            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7671            ## reprocess            ## reprocess
7672            redo B;            next B;
7673          } else {          } else {
7674            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
7675              !!!parse-error (type => 'unmatched end tag',
7676                              text => $token->{tag_name}, token => $token);
7677            ## Ignore the token            ## Ignore the token
7678            !!!next-token;            !!!next-token;
7679            redo B;            next B;
7680          }          }
7681        } elsif ({        } elsif ({
7682                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7683                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7684                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7685                  p => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
7686                    center => 1, datagrid => 1, details => 1, dialog => 1,
7687                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7688                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7689                    ol => 1, pre => 1, section => 1, ul => 1,
7690    
7691                    ## NOTE: As normal, but ... optional tags
7692                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7693                  button => 1, marquee => 1, object => 1,  
7694                    applet => 1, button => 1, marquee => 1, object => 1,
7695                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7696            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7697            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7698            ## </dd>" code.
7699    
7700          ## has an element in scope          ## has an element in scope
7701          my $i;          my $i;
7702          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7703            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7704            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7705              ## 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;  
             }  
7706              $i = $_;              $i = $_;
7707              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7708            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7709                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7710              last INSCOPE;              last INSCOPE;
7711            }            }
7712          } # INSCOPE          } # INSCOPE
7713            
7714          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7715            if (defined $i) {            !!!cp ('t413');
7716              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag',
7717                              text => $token->{tag_name}, token => $token);
7718              ## NOTE: Ignore the token.
7719            } else {
7720              ## Step 1. generate implied end tags
7721              while ({
7722                      ## END_TAG_OPTIONAL_EL
7723                      dd => ($token->{tag_name} ne 'dd'),
7724                      dt => ($token->{tag_name} ne 'dt'),
7725                      li => ($token->{tag_name} ne 'li'),
7726                      option => 1,
7727                      optgroup => 1,
7728                      p => 1,
7729                      rt => 1,
7730                      rp => 1,
7731                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7732                !!!cp ('t409');
7733                pop @{$self->{open_elements}};
7734              }
7735    
7736              ## Step 2.
7737              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7738                      ne $token->{tag_name}) {
7739                !!!cp ('t412');
7740                !!!parse-error (type => 'not closed',
7741                                text => $self->{open_elements}->[-1]->[0]
7742                                    ->manakai_local_name,
7743                                token => $token);
7744            } else {            } else {
7745              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
7746            }            }
7747          }  
7748                      ## Step 3.
         if (defined $i) {  
7749            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7750          } elsif ($token->{tag_name} eq 'p') {  
7751            ## As if <p>, then reprocess the current token            ## Step 4.
7752            my $el;            $clear_up_to_marker->()
7753            !!!create-element ($el, 'p');                if {
7754            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
7755                  }->{$token->{tag_name}};
7756          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
7757          !!!next-token;          !!!next-token;
7758          redo B;          next B;
7759        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7760            ## NOTE: As normal, but interacts with the form element pointer
7761    
7762            undef $self->{form_element};
7763    
7764          ## has an element in scope          ## has an element in scope
7765            my $i;
7766          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7767            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7768            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] == FORM_EL) {
7769              ## generate implied end tags              !!!cp ('t418');
7770              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;  
             }  
7771              last INSCOPE;              last INSCOPE;
7772            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7773                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7774              last INSCOPE;              last INSCOPE;
7775            }            }
7776          } # INSCOPE          } # INSCOPE
7777            
7778          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7779            pop @{$self->{open_elements}};            !!!cp ('t421');
7780          } else {            !!!parse-error (type => 'unmatched end tag',
7781            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
7782              ## NOTE: Ignore the token.
7783            } else {
7784              ## Step 1. generate implied end tags
7785              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7786                !!!cp ('t417');
7787                pop @{$self->{open_elements}};
7788              }
7789              
7790              ## Step 2.
7791              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7792                      ne $token->{tag_name}) {
7793                !!!cp ('t417.1');
7794                !!!parse-error (type => 'not closed',
7795                                text => $self->{open_elements}->[-1]->[0]
7796                                    ->manakai_local_name,
7797                                token => $token);
7798              } else {
7799                !!!cp ('t420');
7800              }  
7801              
7802              ## Step 3.
7803              splice @{$self->{open_elements}}, $i;
7804          }          }
7805    
         undef $self->{form_element};  
7806          !!!next-token;          !!!next-token;
7807          redo B;          next B;
7808        } elsif ({        } elsif ({
7809                    ## NOTE: As normal, except acts as a closer for any ...
7810                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7811                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7812          ## has an element in scope          ## has an element in scope
7813          my $i;          my $i;
7814          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7815            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7816            if ({            if ($node->[1] == HEADING_EL) {
7817                 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;  
             }  
7818              $i = $_;              $i = $_;
7819              last INSCOPE;              last INSCOPE;
7820            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7821                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7822              last INSCOPE;              last INSCOPE;
7823            }            }
7824          } # INSCOPE          } # INSCOPE
7825            
7826          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7827            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t425.1');
7828              !!!parse-error (type => 'unmatched end tag',
7829                              text => $token->{tag_name}, token => $token);
7830              ## NOTE: Ignore the token.
7831            } else {
7832              ## Step 1. generate implied end tags
7833              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7834                !!!cp ('t422');
7835                pop @{$self->{open_elements}};
7836              }
7837              
7838              ## Step 2.
7839              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7840                      ne $token->{tag_name}) {
7841                !!!cp ('t425');
7842                !!!parse-error (type => 'unmatched end tag',
7843                                text => $token->{tag_name}, token => $token);
7844              } else {
7845                !!!cp ('t426');
7846              }
7847    
7848              ## Step 3.
7849              splice @{$self->{open_elements}}, $i;
7850          }          }
7851                    
         splice @{$self->{open_elements}}, $i if defined $i;  
7852          !!!next-token;          !!!next-token;
7853          redo B;          next B;
7854          } elsif ($token->{tag_name} eq 'p') {
7855            ## NOTE: As normal, except </p> implies <p> and ...
7856    
7857            ## has an element in scope
7858            my $non_optional;
7859            my $i;
7860            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7861              my $node = $self->{open_elements}->[$_];
7862              if ($node->[1] == P_EL) {
7863                !!!cp ('t410.1');
7864                $i = $_;
7865                last INSCOPE;
7866              } elsif ($node->[1] & SCOPING_EL) {
7867                !!!cp ('t411.1');
7868                last INSCOPE;
7869              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7870                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7871                !!!cp ('t411.2');
7872                #
7873              } else {
7874                !!!cp ('t411.3');
7875                $non_optional ||= $node;
7876                #
7877              }
7878            } # INSCOPE
7879    
7880            if (defined $i) {
7881              ## 1. Generate implied end tags
7882              #
7883    
7884              ## 2. If current node != "p", parse error
7885              if ($non_optional) {
7886                !!!cp ('t412.1');
7887                !!!parse-error (type => 'not closed',
7888                                text => $non_optional->[0]->manakai_local_name,
7889                                token => $token);
7890              } else {
7891                !!!cp ('t414.1');
7892              }
7893    
7894              ## 3. Pop
7895              splice @{$self->{open_elements}}, $i;
7896            } else {
7897              !!!cp ('t413.1');
7898              !!!parse-error (type => 'unmatched end tag',
7899                              text => $token->{tag_name}, token => $token);
7900    
7901              !!!cp ('t415.1');
7902              ## As if <p>, then reprocess the current token
7903              my $el;
7904              !!!create-element ($el, $HTML_NS, 'p',, $token);
7905              $insert->($el);
7906              ## NOTE: Not inserted into |$self->{open_elements}|.
7907            }
7908    
7909            !!!next-token;
7910            next B;
7911        } elsif ({        } elsif ({
7912                  a => 1,                  a => 1,
7913                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7914                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7915                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7916                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7917          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
7918          redo B;          $formatting_end_tag->($token);
7919            next B;
7920        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7921          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
7922            !!!parse-error (type => 'unmatched end tag',
7923                            text => 'br', token => $token);
7924    
7925          ## As if <br>          ## As if <br>
7926          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7927                    
7928          my $el;          my $el;
7929          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7930          $insert->($el);          $insert->($el);
7931                    
7932          ## Ignore the token.          ## Ignore the token.
7933          !!!next-token;          !!!next-token;
7934          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  
           
7935        } else {        } else {
7936            if ($token->{tag_name} eq 'sarcasm') {
7937              sleep 0.001; # take a deep breath
7938            }
7939    
7940          ## Step 1          ## Step 1
7941          my $node_i = -1;          my $node_i = -1;
7942          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7943    
7944          ## Step 2          ## Step 2
7945          S2: {          S2: {
7946            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7947              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7948              if ($node_tag_name eq $token->{tag_name}) {
7949              ## Step 1              ## Step 1
7950              ## generate implied end tags              ## generate implied end tags
7951              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7952                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
7953                   td => 1, th => 1, tr => 1,                ## NOTE: |<ruby><rt></ruby>|.
7954                   tbody => 1, tfoot => 1, thead => 1,                ## ISSUE: <ruby><rt></rt> will also take this code path,
7955                  }->{$self->{open_elements}->[-1]->[1]}) {                ## which seems wrong.
7956                !!!back-token;                pop @{$self->{open_elements}};
7957                $token = {type => END_TAG_TOKEN,                $node_i++;
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
7958              }              }
7959                    
7960              ## Step 2              ## Step 2
7961              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7962                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7963                $current_tag_name =~ tr/A-Z/a-z/;
7964                if ($current_tag_name ne $token->{tag_name}) {
7965                  !!!cp ('t431');
7966                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
7967                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'not closed',
7968                                  text => $self->{open_elements}->[-1]->[0]
7969                                      ->manakai_local_name,
7970                                  token => $token);
7971                } else {
7972                  !!!cp ('t432');
7973              }              }
7974                            
7975              ## Step 3              ## Step 3
7976              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7977    
7978              !!!next-token;              !!!next-token;
7979              last S2;              last S2;
7980            } else {            } else {
7981              ## Step 3              ## Step 3
7982              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7983                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7984                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7985                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7986                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
7987                  !!!parse-error (type => 'unmatched end tag',
7988                                  text => $token->{tag_name}, token => $token);
7989                ## Ignore the token                ## Ignore the token
7990                !!!next-token;                !!!next-token;
7991                last S2;                last S2;
7992    
7993                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7994                  ## 9.27, "a" is a child of <dd> (conforming).  In
7995                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7996                  ## "a" is a child of both <body> and <dd>.
7997              }              }
7998                
7999                !!!cp ('t434');
8000            }            }
8001                        
8002            ## Step 4            ## Step 4
# Line 5451  sub _tree_construction_main ($) { Line 8006  sub _tree_construction_main ($) {
8006            ## Step 5;            ## Step 5;
8007            redo S2;            redo S2;
8008          } # S2          } # S2
8009          redo B;          next B;
8010        }        }
8011      }      }
8012      redo B;      next B;
8013      } continue { # B
8014        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8015          ## NOTE: The code below is executed in cases where it does not have
8016          ## to be, but it it is harmless even in those cases.
8017          ## has an element in scope
8018          INSCOPE: {
8019            for (reverse 0..$#{$self->{open_elements}}) {
8020              my $node = $self->{open_elements}->[$_];
8021              if ($node->[1] & FOREIGN_EL) {
8022                last INSCOPE;
8023              } elsif ($node->[1] & SCOPING_EL) {
8024                last;
8025              }
8026            }
8027            
8028            ## NOTE: No foreign element in scope.
8029            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8030          } # INSCOPE
8031        }
8032    } # B    } # B
8033    
   ## 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  
   
8034    ## Stop parsing # MUST    ## Stop parsing # MUST
8035        
8036    ## TODO: script stuffs    ## TODO: script stuffs
8037  } # _tree_construct_main  } # _tree_construct_main
8038    
8039  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8040    my $class = shift;    my $class = shift;
8041    my $node = shift;    my $node = shift;
8042    my $s = \$_[0];    #my $s = \$_[0];
8043    my $onerror = $_[1];    my $onerror = $_[1];
8044      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8045    
8046    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
8047    
# Line 5490  sub set_inner_html ($$$) { Line 8060  sub set_inner_html ($$$) {
8060      }      }
8061    
8062      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8063      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8064    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8065      ## TODO: If non-html element      ## TODO: If non-html element
8066    
8067      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8068    
8069    ## TODO: Support for $get_wrapper
8070    
8071      ## Step 1 # MUST      ## Step 1 # MUST
8072      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8073      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5503  sub set_inner_html ($$$) { Line 8075  sub set_inner_html ($$$) {
8075      my $p = $class->new;      my $p = $class->new;
8076      $p->{document} = $doc;      $p->{document} = $doc;
8077    
8078      ## Step 9 # MUST      ## Step 8 # MUST
8079      my $i = 0;      my $i = 0;
8080      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8081      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8082      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8083        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8084        $input = $get_wrapper->($input);
8085        $p->{set_nc} = sub {
8086        my $self = shift;        my $self = shift;
8087    
8088        pop @{$self->{prev_input_character}};        my $char = '';
8089        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8090            $char = $self->{next_nc};
8091            delete $self->{next_nc};
8092            $self->{nc} = ord $char;
8093          } else {
8094            $self->{char_buffer} = '';
8095            $self->{char_buffer_pos} = 0;
8096            
8097            my $count = $input->manakai_read_until
8098                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8099                 $self->{char_buffer_pos});
8100            if ($count) {
8101              $self->{line_prev} = $self->{line};
8102              $self->{column_prev} = $self->{column};
8103              $self->{column}++;
8104              $self->{nc}
8105                  = ord substr ($self->{char_buffer},
8106                                $self->{char_buffer_pos}++, 1);
8107              return;
8108            }
8109            
8110            if ($input->read ($char, 1)) {
8111              $self->{nc} = ord $char;
8112            } else {
8113              $self->{nc} = -1;
8114              return;
8115            }
8116          }
8117    
8118        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8119        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
8120        $column++;  
8121          if ($self->{nc} == 0x000A) { # LF
8122        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
8123          $line++;          $p->{column} = 0;
8124          $column = 0;          !!!cp ('i1');
8125        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8126          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8127          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
8128          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8129          $column = 0;            $self->{next_nc} = $next;
8130        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
8131          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
8132        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
8133            $p->{column} = 0;
8134            !!!cp ('i2');
8135          } elsif ($self->{nc} == 0x0000) { # NULL
8136            !!!cp ('i4');
8137          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8138          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8139        }        }
8140      };      };
8141      $p->{prev_input_character} = [-1, -1, -1];  
8142      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8143              #my ($scalar, $specials_range, $offset) = @_;
8144          return 0 if defined $p->{next_nc};
8145    
8146          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8147          my $offset = $_[2] || 0;
8148          
8149          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8150            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8151            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8152              substr ($_[0], $offset)
8153                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8154              my $count = $+[0] - $-[0];
8155              if ($count) {
8156                $p->{column} += $count;
8157                $p->{char_buffer_pos} += $count;
8158                $p->{line_prev} = $p->{line};
8159                $p->{column_prev} = $p->{column} - 1;
8160                $p->{nc} = -1;
8161              }
8162              return $count;
8163            } else {
8164              return 0;
8165            }
8166          } else {
8167            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8168            if ($count) {
8169              $p->{column} += $count;
8170              $p->{column_prev} += $count;
8171              $p->{nc} = -1;
8172            }
8173            return $count;
8174          }
8175        }; # $p->{read_until}
8176    
8177      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8178        my (%opt) = @_;        my (%opt) = @_;
8179        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8180          my $column = $opt{column};
8181          if (defined $opt{token} and defined $opt{token}->{line}) {
8182            $line = $opt{token}->{line};
8183            $column = $opt{token}->{column};
8184          }
8185          warn "Parse error ($opt{type}) at line $line column $column\n";
8186      };      };
8187      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8188        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8189      };      };
8190            
8191        my $char_onerror = sub {
8192          my (undef, $type, %opt) = @_;
8193          $ponerror->(layer => 'encode',
8194                      line => $p->{line}, column => $p->{column} + 1,
8195                      %opt, type => $type);
8196        }; # $char_onerror
8197        $input->onerror ($char_onerror);
8198    
8199      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8200      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8201    
# Line 5564  sub set_inner_html ($$$) { Line 8217  sub set_inner_html ($$$) {
8217          unless defined $p->{content_model};          unless defined $p->{content_model};
8218          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8219    
8220      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8221          ## TODO: Foreign element OK?
8222    
8223      ## Step 4      ## Step 3
8224      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8225        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8226    
8227      ## Step 5 # MUST      ## Step 4 # MUST
8228      $doc->append_child ($root);      $doc->append_child ($root);
8229    
8230      ## Step 6 # MUST      ## Step 5 # MUST
8231      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8232    
8233      undef $p->{head_element};      undef $p->{head_element};
8234        undef $p->{head_element_inserted};
8235    
8236      ## Step 7 # MUST      ## Step 6 # MUST
8237      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8238    
8239      ## Step 8 # MUST      ## Step 7 # MUST
8240      my $anode = $node;      my $anode = $node;
8241      AN: while (defined $anode) {      AN: while (defined $anode) {
8242        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8243          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8244          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8245            if ($anode->manakai_local_name eq 'form') {            if ($anode->manakai_local_name eq 'form') {
8246                !!!cp ('i5');
8247              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8248              last AN;              last AN;
8249            }            }
# Line 5596  sub set_inner_html ($$$) { Line 8252  sub set_inner_html ($$$) {
8252        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8253      } # AN      } # AN
8254            
8255      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8256      {      {
8257        my $self = $p;        my $self = $p;
8258        !!!next-token;        !!!next-token;
8259      }      }
8260      $p->_tree_construction_main;      $p->_tree_construction_main;
8261    
8262      ## Step 11 # MUST      ## Step 10 # MUST
8263      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8264      for (@cn) {      for (@cn) {
8265        $node->remove_child ($_);        $node->remove_child ($_);
8266      }      }
8267      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8268    
8269      ## Step 12 # MUST      ## Step 11 # MUST
8270      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8271      for (@cn) {      for (@cn) {
8272        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5620  sub set_inner_html ($$$) { Line 8275  sub set_inner_html ($$$) {
8275      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8276    
8277      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8278    
8279        delete $p->{parse_error}; # delete loop
8280    } else {    } else {
8281      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";
8282    }    }

Legend:
Removed from v.1.72  
changed lines
  Added in v.1.206

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24