/[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.56 by wakaba, Sat Aug 11 07:19:18 2007 UTC revision 1.207 by wakaba, Mon Oct 13 08:27:44 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  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);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
23  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
24  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    link => 1,  
31    meta => 1,  ## Bits 12-15
32    hr => 1,  sub SPECIAL_EL () { 0b1_000000000000000 }
33    br => 1,  sub SCOPING_EL () { 0b1_00000000000000 }
34    img=> 1,  sub FORMATTING_EL () { 0b1_0000000000000 }
35    embed => 1,  sub PHRASING_EL () { 0b1_000000000000 }
36    param => 1,  
37    area => 1,  ## Bits 10-11
38    col => 1,  sub FOREIGN_EL () { 0b1_00000000000 }
39    input => 1,  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 DTDD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b010
78    }
79    sub LI_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b100
84    }
85    sub P_EL () {
86      SPECIAL_EL |
87      ADDRESS_DIV_P_EL |
88      END_TAG_OPTIONAL_EL |
89      ALL_END_TAG_OPTIONAL_EL |
90      0b001
91    }
92    
93    sub TABLE_ROW_EL () {
94      SPECIAL_EL |
95      TABLE_ROWS_EL |
96      TABLE_ROW_SCOPING_EL |
97      ALL_END_TAG_OPTIONAL_EL |
98      0b001
99    }
100    sub TABLE_ROW_GROUP_EL () {
101      SPECIAL_EL |
102      TABLE_ROWS_EL |
103      TABLE_ROWS_SCOPING_EL |
104      ALL_END_TAG_OPTIONAL_EL |
105      0b001
106    }
107    
108    sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
109    sub BUTTON_EL () { SCOPING_EL | 0b001 }
110    sub CAPTION_EL () { SCOPING_EL | 0b010 }
111    sub HTML_EL () {
112      SCOPING_EL |
113      TABLE_SCOPING_EL |
114      TABLE_ROWS_SCOPING_EL |
115      TABLE_ROW_SCOPING_EL |
116      ALL_END_TAG_OPTIONAL_EL |
117      0b001
118    }
119    sub TABLE_EL () {
120      SCOPING_EL |
121      TABLE_ROWS_EL |
122      TABLE_SCOPING_EL |
123      0b001
124    }
125    sub TABLE_CELL_EL () {
126      SCOPING_EL |
127      TABLE_ROW_SCOPING_EL |
128      ALL_END_TAG_OPTIONAL_EL |
129      0b001
130    }
131    
132    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
133    sub A_EL () { FORMATTING_EL | 0b001 }
134    sub NOBR_EL () { FORMATTING_EL | 0b010 }
135    
136    sub RUBY_EL () { PHRASING_EL | 0b001 }
137    
138    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
139    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
140    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
141    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
142    
143    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
144    
145    my $el_category = {
146      a => A_EL,
147      address => ADDRESS_DIV_EL,
148      applet => MISC_SCOPING_EL,
149      area => MISC_SPECIAL_EL,
150      article => MISC_SPECIAL_EL,
151      aside => MISC_SPECIAL_EL,
152      b => FORMATTING_EL,
153      base => MISC_SPECIAL_EL,
154      basefont => MISC_SPECIAL_EL,
155      bgsound => MISC_SPECIAL_EL,
156      big => FORMATTING_EL,
157      blockquote => MISC_SPECIAL_EL,
158      body => BODY_EL,
159      br => MISC_SPECIAL_EL,
160      button => BUTTON_EL,
161      caption => CAPTION_EL,
162      center => MISC_SPECIAL_EL,
163      col => MISC_SPECIAL_EL,
164      colgroup => MISC_SPECIAL_EL,
165      command => MISC_SPECIAL_EL,
166      datagrid => MISC_SPECIAL_EL,
167      dd => DTDD_EL,
168      details => MISC_SPECIAL_EL,
169      dialog => MISC_SPECIAL_EL,
170      dir => MISC_SPECIAL_EL,
171      div => ADDRESS_DIV_EL,
172      dl => MISC_SPECIAL_EL,
173      dt => DTDD_EL,
174      em => FORMATTING_EL,
175      embed => MISC_SPECIAL_EL,
176      eventsource => MISC_SPECIAL_EL,
177      fieldset => MISC_SPECIAL_EL,
178      figure => MISC_SPECIAL_EL,
179      font => FORMATTING_EL,
180      footer => MISC_SPECIAL_EL,
181      form => FORM_EL,
182      frame => MISC_SPECIAL_EL,
183      frameset => FRAMESET_EL,
184      h1 => HEADING_EL,
185      h2 => HEADING_EL,
186      h3 => HEADING_EL,
187      h4 => HEADING_EL,
188      h5 => HEADING_EL,
189      h6 => HEADING_EL,
190      head => MISC_SPECIAL_EL,
191      header => MISC_SPECIAL_EL,
192      hr => MISC_SPECIAL_EL,
193      html => HTML_EL,
194      i => FORMATTING_EL,
195      iframe => MISC_SPECIAL_EL,
196      img => MISC_SPECIAL_EL,
197      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
198      input => MISC_SPECIAL_EL,
199      isindex => MISC_SPECIAL_EL,
200      li => LI_EL,
201      link => MISC_SPECIAL_EL,
202      listing => MISC_SPECIAL_EL,
203      marquee => MISC_SCOPING_EL,
204      menu => MISC_SPECIAL_EL,
205      meta => MISC_SPECIAL_EL,
206      nav => MISC_SPECIAL_EL,
207      nobr => NOBR_EL,
208      noembed => MISC_SPECIAL_EL,
209      noframes => MISC_SPECIAL_EL,
210      noscript => MISC_SPECIAL_EL,
211      object => MISC_SCOPING_EL,
212      ol => MISC_SPECIAL_EL,
213      optgroup => OPTGROUP_EL,
214      option => OPTION_EL,
215      p => P_EL,
216      param => MISC_SPECIAL_EL,
217      plaintext => MISC_SPECIAL_EL,
218      pre => MISC_SPECIAL_EL,
219      rp => RUBY_COMPONENT_EL,
220      rt => RUBY_COMPONENT_EL,
221      ruby => RUBY_EL,
222      s => FORMATTING_EL,
223      script => MISC_SPECIAL_EL,
224      select => SELECT_EL,
225      section => MISC_SPECIAL_EL,
226      small => FORMATTING_EL,
227      spacer => MISC_SPECIAL_EL,
228      strike => FORMATTING_EL,
229      strong => FORMATTING_EL,
230      style => MISC_SPECIAL_EL,
231      table => TABLE_EL,
232      tbody => TABLE_ROW_GROUP_EL,
233      td => TABLE_CELL_EL,
234      textarea => MISC_SPECIAL_EL,
235      tfoot => TABLE_ROW_GROUP_EL,
236      th => TABLE_CELL_EL,
237      thead => TABLE_ROW_GROUP_EL,
238      title => MISC_SPECIAL_EL,
239      tr => TABLE_ROW_EL,
240      tt => FORMATTING_EL,
241      u => FORMATTING_EL,
242      ul => MISC_SPECIAL_EL,
243      wbr => MISC_SPECIAL_EL,
244    };
245    
246    my $el_category_f = {
247      $MML_NS => {
248        'annotation-xml' => MML_AXML_EL,
249        mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
250        mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
251        mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
252        ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
253        mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
254      },
255      $SVG_NS => {
256        foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257        desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258        title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      },
260      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
261    };
262    
263    my $svg_attr_name = {
264      attributename => 'attributeName',
265      attributetype => 'attributeType',
266      basefrequency => 'baseFrequency',
267      baseprofile => 'baseProfile',
268      calcmode => 'calcMode',
269      clippathunits => 'clipPathUnits',
270      contentscripttype => 'contentScriptType',
271      contentstyletype => 'contentStyleType',
272      diffuseconstant => 'diffuseConstant',
273      edgemode => 'edgeMode',
274      externalresourcesrequired => 'externalResourcesRequired',
275      filterres => 'filterRes',
276      filterunits => 'filterUnits',
277      glyphref => 'glyphRef',
278      gradienttransform => 'gradientTransform',
279      gradientunits => 'gradientUnits',
280      kernelmatrix => 'kernelMatrix',
281      kernelunitlength => 'kernelUnitLength',
282      keypoints => 'keyPoints',
283      keysplines => 'keySplines',
284      keytimes => 'keyTimes',
285      lengthadjust => 'lengthAdjust',
286      limitingconeangle => 'limitingConeAngle',
287      markerheight => 'markerHeight',
288      markerunits => 'markerUnits',
289      markerwidth => 'markerWidth',
290      maskcontentunits => 'maskContentUnits',
291      maskunits => 'maskUnits',
292      numoctaves => 'numOctaves',
293      pathlength => 'pathLength',
294      patterncontentunits => 'patternContentUnits',
295      patterntransform => 'patternTransform',
296      patternunits => 'patternUnits',
297      pointsatx => 'pointsAtX',
298      pointsaty => 'pointsAtY',
299      pointsatz => 'pointsAtZ',
300      preservealpha => 'preserveAlpha',
301      preserveaspectratio => 'preserveAspectRatio',
302      primitiveunits => 'primitiveUnits',
303      refx => 'refX',
304      refy => 'refY',
305      repeatcount => 'repeatCount',
306      repeatdur => 'repeatDur',
307      requiredextensions => 'requiredExtensions',
308      requiredfeatures => 'requiredFeatures',
309      specularconstant => 'specularConstant',
310      specularexponent => 'specularExponent',
311      spreadmethod => 'spreadMethod',
312      startoffset => 'startOffset',
313      stddeviation => 'stdDeviation',
314      stitchtiles => 'stitchTiles',
315      surfacescale => 'surfaceScale',
316      systemlanguage => 'systemLanguage',
317      tablevalues => 'tableValues',
318      targetx => 'targetX',
319      targety => 'targetY',
320      textlength => 'textLength',
321      viewbox => 'viewBox',
322      viewtarget => 'viewTarget',
323      xchannelselector => 'xChannelSelector',
324      ychannelselector => 'yChannelSelector',
325      zoomandpan => 'zoomAndPan',
326    };
327    
328    my $foreign_attr_xname = {
329      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
330      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
331      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
332      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
333      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
334      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
335      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
336      'xml:base' => [$XML_NS, ['xml', 'base']],
337      'xml:lang' => [$XML_NS, ['xml', 'lang']],
338      'xml:space' => [$XML_NS, ['xml', 'space']],
339      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
340      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
341  };  };
342    
343  my $c1_entity_char = {  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
344    
345    my $charref_map = {
346      0x0D => 0x000A,
347    0x80 => 0x20AC,    0x80 => 0x20AC,
348    0x81 => 0xFFFD,    0x81 => 0xFFFD,
349    0x82 => 0x201A,    0x82 => 0x201A,
# Line 60  my $c1_entity_char = { Line 376  my $c1_entity_char = {
376    0x9D => 0xFFFD,    0x9D => 0xFFFD,
377    0x9E => 0x017E,    0x9E => 0x017E,
378    0x9F => 0x0178,    0x9F => 0x0178,
379  }; # $c1_entity_char  }; # $charref_map
380    $charref_map->{$_} = 0xFFFD
381        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
382            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
383            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
384            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
385            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
386            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
387            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
388    
389  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
390    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
391    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
392    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
393    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
394    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
395    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
396    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
397    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
398    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
399    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
400  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
401  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
402    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
403    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
404  };  
405  my $formatting_category = {    my $onerror = $_[2] || sub {
406    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,      my (%opt) = @_;
407    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,      warn "Parse error ($opt{type})\n";
408  };    };
409  # $phrasing_category: all other elements    $self->{parse_error} = $onerror; # updated later by parse_char_string
410    
411      my $get_wrapper = $_[3] || sub ($) {
412        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
413      };
414    
415      ## HTML5 encoding sniffing algorithm
416      require Message::Charset::Info;
417      my $charset;
418      my $buffer;
419      my ($char_stream, $e_status);
420    
421      SNIFFING: {
422        ## NOTE: By setting |allow_fallback| option true when the
423        ## |get_decode_handle| method is invoked, we ignore what the HTML5
424        ## spec requires, i.e. unsupported encoding should be ignored.
425          ## TODO: We should not do this unless the parser is invoked
426          ## in the conformance checking mode, in which this behavior
427          ## would be useful.
428    
429        ## Step 1
430        if (defined $charset_name) {
431          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
432              ## TODO: Is this ok?  Transfer protocol's parameter should be
433              ## interpreted in its semantics?
434    
435          ($char_stream, $e_status) = $charset->get_decode_handle
436              ($byte_stream, allow_error_reporting => 1,
437               allow_fallback => 1);
438          if ($char_stream) {
439            $self->{confident} = 1;
440            last SNIFFING;
441          } else {
442            !!!parse-error (type => 'charset:not supported',
443                            layer => 'encode',
444                            line => 1, column => 1,
445                            value => $charset_name,
446                            level => $self->{level}->{uncertain});
447          }
448        }
449    
450        ## Step 2
451        my $byte_buffer = '';
452        for (1..1024) {
453          my $char = $byte_stream->getc;
454          last unless defined $char;
455          $byte_buffer .= $char;
456        } ## TODO: timeout
457    
458        ## Step 3
459        if ($byte_buffer =~ /^\xFE\xFF/) {
460          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
461          ($char_stream, $e_status) = $charset->get_decode_handle
462              ($byte_stream, allow_error_reporting => 1,
463               allow_fallback => 1, byte_buffer => \$byte_buffer);
464          $self->{confident} = 1;
465          last SNIFFING;
466        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
467          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
468          ($char_stream, $e_status) = $charset->get_decode_handle
469              ($byte_stream, allow_error_reporting => 1,
470               allow_fallback => 1, byte_buffer => \$byte_buffer);
471          $self->{confident} = 1;
472          last SNIFFING;
473        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
474          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
475          ($char_stream, $e_status) = $charset->get_decode_handle
476              ($byte_stream, allow_error_reporting => 1,
477               allow_fallback => 1, byte_buffer => \$byte_buffer);
478          $self->{confident} = 1;
479          last SNIFFING;
480        }
481    
482        ## Step 4
483        ## TODO: <meta charset>
484    
485        ## Step 5
486        ## TODO: from history
487    
488        ## Step 6
489        require Whatpm::Charset::UniversalCharDet;
490        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
491            ($byte_buffer);
492        if (defined $charset_name) {
493          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
494    
495          require Whatpm::Charset::DecodeHandle;
496          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
497              ($byte_stream);
498          ($char_stream, $e_status) = $charset->get_decode_handle
499              ($buffer, allow_error_reporting => 1,
500               allow_fallback => 1, byte_buffer => \$byte_buffer);
501          if ($char_stream) {
502            $buffer->{buffer} = $byte_buffer;
503            !!!parse-error (type => 'sniffing:chardet',
504                            text => $charset_name,
505                            level => $self->{level}->{info},
506                            layer => 'encode',
507                            line => 1, column => 1);
508            $self->{confident} = 0;
509            last SNIFFING;
510          }
511        }
512    
513        ## Step 7: default
514        ## TODO: Make this configurable.
515        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
516            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
517            ## detectable in the step 6.
518        require Whatpm::Charset::DecodeHandle;
519        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
520            ($byte_stream);
521        ($char_stream, $e_status)
522            = $charset->get_decode_handle ($buffer,
523                                           allow_error_reporting => 1,
524                                           allow_fallback => 1,
525                                           byte_buffer => \$byte_buffer);
526        $buffer->{buffer} = $byte_buffer;
527        !!!parse-error (type => 'sniffing:default',
528                        text => 'windows-1252',
529                        level => $self->{level}->{info},
530                        line => 1, column => 1,
531                        layer => 'encode');
532        $self->{confident} = 0;
533      } # SNIFFING
534    
535      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
536        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
537        !!!parse-error (type => 'chardecode:fallback',
538                        #text => $self->{input_encoding},
539                        level => $self->{level}->{uncertain},
540                        line => 1, column => 1,
541                        layer => 'encode');
542      } elsif (not ($e_status &
543                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
544        $self->{input_encoding} = $charset->get_iana_name;
545        !!!parse-error (type => 'chardecode:no error',
546                        text => $self->{input_encoding},
547                        level => $self->{level}->{uncertain},
548                        line => 1, column => 1,
549                        layer => 'encode');
550      } else {
551        $self->{input_encoding} = $charset->get_iana_name;
552      }
553    
554      $self->{change_encoding} = sub {
555        my $self = shift;
556        $charset_name = shift;
557        my $token = shift;
558    
559        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
560        ($char_stream, $e_status) = $charset->get_decode_handle
561            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
562             byte_buffer => \ $buffer->{buffer});
563        
564        if ($char_stream) { # if supported
565          ## "Change the encoding" algorithm:
566    
567          ## Step 1    
568          if ($charset->{category} &
569              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
570            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
571            ($char_stream, $e_status) = $charset->get_decode_handle
572                ($byte_stream,
573                 byte_buffer => \ $buffer->{buffer});
574          }
575          $charset_name = $charset->get_iana_name;
576          
577          ## Step 2
578          if (defined $self->{input_encoding} and
579              $self->{input_encoding} eq $charset_name) {
580            !!!parse-error (type => 'charset label:matching',
581                            text => $charset_name,
582                            level => $self->{level}->{info});
583            $self->{confident} = 1;
584            return;
585          }
586    
587  sub parse_string ($$$;$) {        !!!parse-error (type => 'charset label detected',
588    my $self = shift->new;                        text => $self->{input_encoding},
589    my $s = \$_[0];                        value => $charset_name,
590                          level => $self->{level}->{warn},
591                          token => $token);
592          
593          ## Step 3
594          # if (can) {
595            ## change the encoding on the fly.
596            #$self->{confident} = 1;
597            #return;
598          # }
599          
600          ## Step 4
601          throw Whatpm::HTML::RestartParser ();
602        }
603      }; # $self->{change_encoding}
604    
605      my $char_onerror = sub {
606        my (undef, $type, %opt) = @_;
607        !!!parse-error (layer => 'encode',
608                        line => $self->{line}, column => $self->{column} + 1,
609                        %opt, type => $type);
610        if ($opt{octets}) {
611          ${$opt{octets}} = "\x{FFFD}"; # relacement character
612        }
613      };
614    
615      my $wrapped_char_stream = $get_wrapper->($char_stream);
616      $wrapped_char_stream->onerror ($char_onerror);
617    
618      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
619      my $return;
620      try {
621        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
622      } catch Whatpm::HTML::RestartParser with {
623        ## NOTE: Invoked after {change_encoding}.
624    
625        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
626          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
627          !!!parse-error (type => 'chardecode:fallback',
628                          level => $self->{level}->{uncertain},
629                          #text => $self->{input_encoding},
630                          line => 1, column => 1,
631                          layer => 'encode');
632        } elsif (not ($e_status &
633                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
634          $self->{input_encoding} = $charset->get_iana_name;
635          !!!parse-error (type => 'chardecode:no error',
636                          text => $self->{input_encoding},
637                          level => $self->{level}->{uncertain},
638                          line => 1, column => 1,
639                          layer => 'encode');
640        } else {
641          $self->{input_encoding} = $charset->get_iana_name;
642        }
643        $self->{confident} = 1;
644    
645        $wrapped_char_stream = $get_wrapper->($char_stream);
646        $wrapped_char_stream->onerror ($char_onerror);
647    
648        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
649      };
650      return $return;
651    } # parse_byte_stream
652    
653    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
654    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
655    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
656    ## because the core part of our HTML parser expects a string of character,
657    ## not a string of bytes or code units or anything which might contain a BOM.
658    ## Therefore, any parser interface that accepts a string of bytes,
659    ## such as |parse_byte_string| in this module, must ensure that it does
660    ## strip the BOM and never strip any ZWNBSP.
661    
662    sub parse_char_string ($$$;$$) {
663      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
664      my $self = shift;
665      my $s = ref $_[0] ? $_[0] : \($_[0]);
666      require Whatpm::Charset::DecodeHandle;
667      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
668      return $self->parse_char_stream ($input, @_[1..$#_]);
669    } # parse_char_string
670    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
671    
672    sub parse_char_stream ($$$;$$) {
673      my $self = ref $_[0] ? shift : shift->new;
674      my $input = $_[0];
675    $self->{document} = $_[1];    $self->{document} = $_[1];
676      @{$self->{document}->child_nodes} = ();
677    
678    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
679    
680    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
681    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
682    my $column = 0;        if defined $self->{input_encoding};
683    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
684    
685      $self->{line_prev} = $self->{line} = 1;
686      $self->{column_prev} = -1;
687      $self->{column} = 0;
688      $self->{set_nc} = sub {
689      my $self = shift;      my $self = shift;
690    
691      pop @{$self->{prev_input_character}};      my $char = '';
692      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
693          $char = $self->{next_nc};
694          delete $self->{next_nc};
695          $self->{nc} = ord $char;
696        } else {
697          $self->{char_buffer} = '';
698          $self->{char_buffer_pos} = 0;
699    
700          my $count = $input->manakai_read_until
701             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
702          if ($count) {
703            $self->{line_prev} = $self->{line};
704            $self->{column_prev} = $self->{column};
705            $self->{column}++;
706            $self->{nc}
707                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
708            return;
709          }
710    
711          if ($input->read ($char, 1)) {
712            $self->{nc} = ord $char;
713          } else {
714            $self->{nc} = -1;
715            return;
716          }
717        }
718    
719      $self->{next_input_character} = -1 and return if $i >= length $$s;      ($self->{line_prev}, $self->{column_prev})
720      $self->{next_input_character} = ord substr $$s, $i++, 1;          = ($self->{line}, $self->{column});
721      $column++;      $self->{column}++;
722            
723      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
724        $line++;        !!!cp ('j1');
725        $column = 0;        $self->{line}++;
726      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
727        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
728        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
729        $line++;  ## TODO: support for abort/streaming
730        $column = 0;        my $next = '';
731      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
732        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
733      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
734          $self->{nc} = 0x000A; # LF # MUST
735          $self->{line}++;
736          $self->{column} = 0;
737        } elsif ($self->{nc} == 0x0000) { # NULL
738          !!!cp ('j4');
739        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
740        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
741      }      }
742    };    };
743    $self->{prev_input_character} = [-1, -1, -1];  
744    $self->{next_input_character} = -1;    $self->{read_until} = sub {
745        #my ($scalar, $specials_range, $offset) = @_;
746        return 0 if defined $self->{next_nc};
747    
748        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
749        my $offset = $_[2] || 0;
750    
751        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
752          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
753          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
754            substr ($_[0], $offset)
755                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
756            my $count = $+[0] - $-[0];
757            if ($count) {
758              $self->{column} += $count;
759              $self->{char_buffer_pos} += $count;
760              $self->{line_prev} = $self->{line};
761              $self->{column_prev} = $self->{column} - 1;
762              $self->{nc} = -1;
763            }
764            return $count;
765          } else {
766            return 0;
767          }
768        } else {
769          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
770          if ($count) {
771            $self->{column} += $count;
772            $self->{line_prev} = $self->{line};
773            $self->{column_prev} = $self->{column} - 1;
774            $self->{nc} = -1;
775          }
776          return $count;
777        }
778      }; # $self->{read_until}
779    
780    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
781      my (%opt) = @_;      my (%opt) = @_;
782      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
783        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
784        warn "Parse error ($opt{type}) at line $line column $column\n";
785    };    };
786    $self->{parse_error} = sub {    $self->{parse_error} = sub {
787      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
788    };    };
789    
790      my $char_onerror = sub {
791        my (undef, $type, %opt) = @_;
792        !!!parse-error (layer => 'encode',
793                        line => $self->{line}, column => $self->{column} + 1,
794                        %opt, type => $type);
795      }; # $char_onerror
796    
797      if ($_[3]) {
798        $input = $_[3]->($input);
799        $input->onerror ($char_onerror);
800      } else {
801        $input->onerror ($char_onerror) unless defined $input->onerror;
802      }
803    
804    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
805    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
806    $self->_construct_tree;    $self->_construct_tree;
807    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
808    
809      delete $self->{parse_error}; # remove loop
810    
811    return $self->{document};    return $self->{document};
812  } # parse_string  } # parse_char_stream
813    
814  sub new ($) {  sub new ($) {
815    my $class = shift;    my $class = shift;
816    my $self = bless {}, $class;    my $self = bless {
817    $self->{set_next_input_character} = sub {      level => {must => 'm',
818      $self->{next_input_character} = -1;                should => 's',
819                  warn => 'w',
820                  info => 'i',
821                  uncertain => 'u'},
822      }, $class;
823      $self->{set_nc} = sub {
824        $self->{nc} = -1;
825    };    };
826    $self->{parse_error} = sub {    $self->{parse_error} = sub {
827      #      #
828    };    };
829      $self->{change_encoding} = sub {
830        # if ($_[0] is a supported encoding) {
831        #   run "change the encoding" algorithm;
832        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
833        # }
834      };
835      $self->{application_cache_selection} = sub {
836        #
837      };
838    return $self;    return $self;
839  } # new  } # new
840    
# Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_ Line 847  sub CDATA_CONTENT_MODEL () { CM_LIMITED_
847  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
848  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
849    
850    sub DATA_STATE () { 0 }
851    #sub ENTITY_DATA_STATE () { 1 }
852    sub TAG_OPEN_STATE () { 2 }
853    sub CLOSE_TAG_OPEN_STATE () { 3 }
854    sub TAG_NAME_STATE () { 4 }
855    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
856    sub ATTRIBUTE_NAME_STATE () { 6 }
857    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
858    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
859    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
860    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
861    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
862    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
863    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
864    sub COMMENT_START_STATE () { 14 }
865    sub COMMENT_START_DASH_STATE () { 15 }
866    sub COMMENT_STATE () { 16 }
867    sub COMMENT_END_STATE () { 17 }
868    sub COMMENT_END_DASH_STATE () { 18 }
869    sub BOGUS_COMMENT_STATE () { 19 }
870    sub DOCTYPE_STATE () { 20 }
871    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
872    sub DOCTYPE_NAME_STATE () { 22 }
873    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
874    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
875    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
876    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
877    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
878    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
879    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
880    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
881    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
882    sub BOGUS_DOCTYPE_STATE () { 32 }
883    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
884    sub SELF_CLOSING_START_TAG_STATE () { 34 }
885    sub CDATA_SECTION_STATE () { 35 }
886    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
887    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
888    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
889    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
890    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
891    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
892    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
893    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
894    ## NOTE: "Entity data state", "entity in attribute value state", and
895    ## "consume a character reference" algorithm are jointly implemented
896    ## using the following six states:
897    sub ENTITY_STATE () { 44 }
898    sub ENTITY_HASH_STATE () { 45 }
899    sub NCR_NUM_STATE () { 46 }
900    sub HEXREF_X_STATE () { 47 }
901    sub HEXREF_HEX_STATE () { 48 }
902    sub ENTITY_NAME_STATE () { 49 }
903    sub PCDATA_STATE () { 50 } # "data state" in the spec
904    
905  sub DOCTYPE_TOKEN () { 1 }  sub DOCTYPE_TOKEN () { 1 }
906  sub COMMENT_TOKEN () { 2 }  sub COMMENT_TOKEN () { 2 }
907  sub START_TAG_TOKEN () { 3 }  sub START_TAG_TOKEN () { 3 }
# Line 174  sub TABLE_IMS ()      { 0b1000000 } Line 917  sub TABLE_IMS ()      { 0b1000000 }
917  sub ROW_IMS ()        { 0b10000000 }  sub ROW_IMS ()        { 0b10000000 }
918  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
919  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
920    sub SELECT_IMS ()     { 0b10000000000 }
921    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
922        ## NOTE: "in foreign content" insertion mode is special; it is combined
923        ## with the secondary insertion mode.  In this parser, they are stored
924        ## together in the bit-or'ed form.
925    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
926        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
927        ## combined with the original insertion mode.  In thie parser,
928        ## they are stored together in the bit-or'ed form.
929    
930    ## NOTE: "initial" and "before html" insertion modes have no constants.
931    
932    ## NOTE: "after after body" insertion mode.
933  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }  sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
934    
935    ## NOTE: "after after frameset" insertion mode.
936  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }  sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
937    
938  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }  sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
939  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }  sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
940  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }  sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
# Line 190  sub IN_TABLE_IM () { TABLE_IMS } Line 948  sub IN_TABLE_IM () { TABLE_IMS }
948  sub AFTER_BODY_IM () { BODY_AFTER_IMS }  sub AFTER_BODY_IM () { BODY_AFTER_IMS }
949  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }  sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
950  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }  sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
951  sub IN_SELECT_IM () { 0b01 }  sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
952    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
953  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
954    
955  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
956    
957  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
958    my $self = shift;    my $self = shift;
959    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
960      #$self->{s_kwd}; # state keyword - initialized when used
961      #$self->{entity__value}; # initialized when used
962      #$self->{entity__match}; # initialized when used
963    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
964    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
965    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
966    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
967    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
968    $self->{char} = [];    delete $self->{self_closing};
969    # $self->{next_input_character}    $self->{char_buffer} = '';
970      $self->{char_buffer_pos} = 0;
971      $self->{nc} = -1; # next input character
972      #$self->{next_nc}
973    !!!next-input-character;    !!!next-input-character;
974    $self->{token} = [];    $self->{token} = [];
975    # $self->{escape}    # $self->{escape}
# Line 215  sub _initialize_tokenizer ($) { Line 980  sub _initialize_tokenizer ($) {
980  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
981  ##   ->{name} (DOCTYPE_TOKEN)  ##   ->{name} (DOCTYPE_TOKEN)
982  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
983  ##   ->{public_identifier} (DOCTYPE_TOKEN)  ##   ->{pubid} (DOCTYPE_TOKEN)
984  ##   ->{system_identifier} (DOCTYPE_TOKEN)  ##   ->{sysid} (DOCTYPE_TOKEN)
985  ##   ->{correct} == 1 or 0 (DOCTYPE_TOKEN)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
986  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
987    ##        ->{name}
988    ##        ->{value}
989    ##        ->{has_reference} == 1 or 0
990  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
991    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
992    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
993    ##     while the token is pushed back to the stack.
994    
995  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
996    
# Line 229  sub _initialize_tokenizer ($) { Line 1000  sub _initialize_tokenizer ($) {
1000  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1001  ## and removed from the list.  ## and removed from the list.
1002    
1003    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1004    ## (This requirement was dropped from HTML5 spec, unfortunately.)
1005    
1006    my $is_space = {
1007      0x0009 => 1, # CHARACTER TABULATION (HT)
1008      0x000A => 1, # LINE FEED (LF)
1009      #0x000B => 0, # LINE TABULATION (VT)
1010      0x000C => 1, # FORM FEED (FF)
1011      #0x000D => 1, # CARRIAGE RETURN (CR)
1012      0x0020 => 1, # SPACE (SP)
1013    };
1014    
1015  sub _get_next_token ($) {  sub _get_next_token ($) {
1016    my $self = shift;    my $self = shift;
1017    
1018      if ($self->{self_closing}) {
1019        !!!parse-error (type => 'nestc', token => $self->{ct});
1020        ## NOTE: The |self_closing| flag is only set by start tag token.
1021        ## In addition, when a start tag token is emitted, it is always set to
1022        ## |ct|.
1023        delete $self->{self_closing};
1024      }
1025    
1026    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1027        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1028      return shift @{$self->{token}};      return shift @{$self->{token}};
1029    }    }
1030    
1031    A: {    A: {
1032      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1033        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1034          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA  
1035            $self->{state} = 'entity data';        if ($self->{nc} == 0x0026) { # &
1036            !!!cp (0.1);
1037            ## NOTE: In the spec, the tokenizer is switched to the
1038            ## "entity data state".  In this implementation, the tokenizer
1039            ## is switched to the |ENTITY_STATE|, which is an implementation
1040            ## of the "consume a character reference" algorithm.
1041            $self->{entity_add} = -1;
1042            $self->{prev_state} = DATA_STATE;
1043            $self->{state} = ENTITY_STATE;
1044            !!!next-input-character;
1045            redo A;
1046          } elsif ($self->{nc} == 0x003C) { # <
1047            !!!cp (0.2);
1048            $self->{state} = TAG_OPEN_STATE;
1049            !!!next-input-character;
1050            redo A;
1051          } elsif ($self->{nc} == -1) {
1052            !!!cp (0.3);
1053            !!!emit ({type => END_OF_FILE_TOKEN,
1054                      line => $self->{line}, column => $self->{column}});
1055            last A; ## TODO: ok?
1056          } else {
1057            !!!cp (0.4);
1058            #
1059          }
1060    
1061          # Anything else
1062          my $token = {type => CHARACTER_TOKEN,
1063                       data => chr $self->{nc},
1064                       line => $self->{line}, column => $self->{column},
1065                      };
1066          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1067    
1068          ## Stay in the state.
1069          !!!next-input-character;
1070          !!!emit ($token);
1071          redo A;
1072        } elsif ($self->{state} == DATA_STATE) {
1073          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1074          if ($self->{nc} == 0x0026) { # &
1075            $self->{s_kwd} = '';
1076            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1077                not $self->{escape}) {
1078              !!!cp (1);
1079              ## NOTE: In the spec, the tokenizer is switched to the
1080              ## "entity data state".  In this implementation, the tokenizer
1081              ## is switched to the |ENTITY_STATE|, which is an implementation
1082              ## of the "consume a character reference" algorithm.
1083              $self->{entity_add} = -1;
1084              $self->{prev_state} = DATA_STATE;
1085              $self->{state} = ENTITY_STATE;
1086            !!!next-input-character;            !!!next-input-character;
1087            redo A;            redo A;
1088          } else {          } else {
1089              !!!cp (2);
1090            #            #
1091          }          }
1092        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1093          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1094            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1095              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1096                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1097                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1098                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1099              }              $self->{s_kwd} = '--';
1100                #
1101              } elsif ($self->{s_kwd} eq '---') {
1102                !!!cp (4);
1103                $self->{s_kwd} = '--';
1104                #
1105              } else {
1106                !!!cp (5);
1107                #
1108            }            }
1109          }          }
1110                    
1111          #          #
1112        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1113            if (length $self->{s_kwd}) {
1114              !!!cp (5.1);
1115              $self->{s_kwd} .= '!';
1116              #
1117            } else {
1118              !!!cp (5.2);
1119              #$self->{s_kwd} = '';
1120              #
1121            }
1122            #
1123          } elsif ($self->{nc} == 0x003C) { # <
1124          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1125              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1126               not $self->{escape})) {               not $self->{escape})) {
1127            $self->{state} = 'tag open';            !!!cp (6);
1128              $self->{state} = TAG_OPEN_STATE;
1129            !!!next-input-character;            !!!next-input-character;
1130            redo A;            redo A;
1131          } else {          } else {
1132              !!!cp (7);
1133              $self->{s_kwd} = '';
1134            #            #
1135          }          }
1136        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1137          if ($self->{escape} and          if ($self->{escape} and
1138              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1139            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1140                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1141              delete $self->{escape};              delete $self->{escape};
1142              } else {
1143                !!!cp (9);
1144            }            }
1145            } else {
1146              !!!cp (10);
1147          }          }
1148                    
1149            $self->{s_kwd} = '';
1150          #          #
1151        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1152          !!!emit ({type => END_OF_FILE_TOKEN});          !!!cp (11);
1153            $self->{s_kwd} = '';
1154            !!!emit ({type => END_OF_FILE_TOKEN,
1155                      line => $self->{line}, column => $self->{column}});
1156          last A; ## TODO: ok?          last A; ## TODO: ok?
1157          } else {
1158            !!!cp (12);
1159            $self->{s_kwd} = '';
1160            #
1161        }        }
1162    
1163        # Anything else        # Anything else
1164        my $token = {type => CHARACTER_TOKEN,        my $token = {type => CHARACTER_TOKEN,
1165                     data => chr $self->{next_input_character}};                     data => chr $self->{nc},
1166        ## Stay in the data state                     line => $self->{line}, column => $self->{column},
1167        !!!next-input-character;                    };
1168          if ($self->{read_until}->($token->{data}, q[-!<>&],
1169        !!!emit ($token);                                  length $token->{data})) {
1170            $self->{s_kwd} = '';
1171        redo A;        }
     } elsif ($self->{state} eq 'entity data') {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0);  
   
       $self->{state} = 'data';  
       # next-input-character is already done  
1172    
1173        unless (defined $token) {        ## Stay in the data state.
1174          !!!emit ({type => CHARACTER_TOKEN, data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1175            !!!cp (13);
1176            $self->{state} = PCDATA_STATE;
1177        } else {        } else {
1178          !!!emit ($token);          !!!cp (14);
1179            ## Stay in the state.
1180        }        }
1181          !!!next-input-character;
1182          !!!emit ($token);
1183        redo A;        redo A;
1184      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1185        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1186          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1187              !!!cp (15);
1188            !!!next-input-character;            !!!next-input-character;
1189            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1190            redo A;            redo A;
1191            } elsif ($self->{nc} == 0x0021) { # !
1192              !!!cp (15.1);
1193              $self->{s_kwd} = '<' unless $self->{escape};
1194              #
1195          } else {          } else {
1196            ## reconsume            !!!cp (16);
1197            $self->{state} = 'data';            #
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<'});  
   
           redo A;  
1198          }          }
1199    
1200            ## reconsume
1201            $self->{state} = DATA_STATE;
1202            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1203                      line => $self->{line_prev},
1204                      column => $self->{column_prev},
1205                     });
1206            redo A;
1207        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1208          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1209            $self->{state} = 'markup declaration open';            !!!cp (17);
1210              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1211            !!!next-input-character;            !!!next-input-character;
1212            redo A;            redo A;
1213          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1214            $self->{state} = 'close tag open';            !!!cp (18);
1215              $self->{state} = CLOSE_TAG_OPEN_STATE;
1216            !!!next-input-character;            !!!next-input-character;
1217            redo A;            redo A;
1218          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1219                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1220            $self->{current_token}            !!!cp (19);
1221              $self->{ct}
1222              = {type => START_TAG_TOKEN,              = {type => START_TAG_TOKEN,
1223                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{nc} + 0x0020),
1224            $self->{state} = 'tag name';                 line => $self->{line_prev},
1225                   column => $self->{column_prev}};
1226              $self->{state} = TAG_NAME_STATE;
1227            !!!next-input-character;            !!!next-input-character;
1228            redo A;            redo A;
1229          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1230                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1231            $self->{current_token} = {type => START_TAG_TOKEN,            !!!cp (20);
1232                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1233            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1234                                        line => $self->{line_prev},
1235                                        column => $self->{column_prev}};
1236              $self->{state} = TAG_NAME_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238            redo A;            redo A;
1239          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1240            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1241            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1242                              line => $self->{line_prev},
1243                              column => $self->{column_prev});
1244              $self->{state} = DATA_STATE;
1245            !!!next-input-character;            !!!next-input-character;
1246    
1247            !!!emit ({type => CHARACTER_TOKEN, data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1248                        line => $self->{line_prev},
1249                        column => $self->{column_prev},
1250                       });
1251    
1252            redo A;            redo A;
1253          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1254            !!!parse-error (type => 'pio');            !!!cp (22);
1255            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1256            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1257                              column => $self->{column_prev});
1258              $self->{state} = BOGUS_COMMENT_STATE;
1259              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1260                                        line => $self->{line_prev},
1261                                        column => $self->{column_prev},
1262                                       };
1263              ## $self->{nc} is intentionally left as is
1264            redo A;            redo A;
1265          } else {          } else {
1266            !!!parse-error (type => 'bare stago');            !!!cp (23);
1267            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1268                              line => $self->{line_prev},
1269                              column => $self->{column_prev});
1270              $self->{state} = DATA_STATE;
1271            ## reconsume            ## reconsume
1272    
1273            !!!emit ({type => CHARACTER_TOKEN, data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1274                        line => $self->{line_prev},
1275                        column => $self->{column_prev},
1276                       });
1277    
1278            redo A;            redo A;
1279          }          }
1280        } else {        } else {
1281          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1282        }        }
1283      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1284        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1285          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';  
1286    
1287                !!!emit ({type => CHARACTER_TOKEN, data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1288            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1289                redo A;          if (defined $self->{last_stag_name}) {
1290              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1291            }            $self->{s_kwd} = '';
1292            push @next_char, $self->{next_input_character};            ## Reconsume.
1293                    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';  
             !!!emit ({type => CHARACTER_TOKEN, data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1294          } else {          } else {
1295            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1296            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1297            $self->{state} = 'data';            !!!cp (28);
1298            !!!emit ({type => CHARACTER_TOKEN, data => '</'});            $self->{state} = DATA_STATE;
1299              ## Reconsume.
1300              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1301                        line => $l, column => $c,
1302                       });
1303            redo A;            redo A;
1304          }          }
1305        }        }
1306          
1307        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1308            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1309          $self->{current_token} = {type => END_TAG_TOKEN,          !!!cp (29);
1310                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1311          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1312          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1313          redo A;                 line => $l, column => $c};
1314        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1315                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1316          $self->{current_token} = {type => END_TAG_TOKEN,          redo A;
1317                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1318          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1319          !!!next-input-character;          !!!cp (30);
1320          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1321        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1322          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1323          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1324            !!!next-input-character;
1325            redo A;
1326          } elsif ($self->{nc} == 0x003E) { # >
1327            !!!cp (31);
1328            !!!parse-error (type => 'empty end tag',
1329                            line => $self->{line_prev}, ## "<" in "</>"
1330                            column => $self->{column_prev} - 1);
1331            $self->{state} = DATA_STATE;
1332          !!!next-input-character;          !!!next-input-character;
1333          redo A;          redo A;
1334        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1335            !!!cp (32);
1336          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1337          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1338          # reconsume          # reconsume
1339    
1340          !!!emit ({type => CHARACTER_TOKEN, data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1341                      line => $l, column => $c,
1342                     });
1343    
1344          redo A;          redo A;
1345        } else {        } else {
1346            !!!cp (33);
1347          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1348          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1349          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1350          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1351                                      column => $self->{column_prev} - 1,
1352                                     };
1353            ## NOTE: $self->{nc} is intentionally left as is.
1354            ## Although the "anything else" case of the spec not explicitly
1355            ## states that the next input character is to be reconsumed,
1356            ## it will be included to the |data| of the comment token
1357            ## generated from the bogus end tag, as defined in the
1358            ## "bogus comment state" entry.
1359            redo A;
1360          }
1361        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1362          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1363          if (length $ch) {
1364            my $CH = $ch;
1365            $ch =~ tr/a-z/A-Z/;
1366            my $nch = chr $self->{nc};
1367            if ($nch eq $ch or $nch eq $CH) {
1368              !!!cp (24);
1369              ## Stay in the state.
1370              $self->{s_kwd} .= $nch;
1371              !!!next-input-character;
1372              redo A;
1373            } else {
1374              !!!cp (25);
1375              $self->{state} = DATA_STATE;
1376              ## Reconsume.
1377              !!!emit ({type => CHARACTER_TOKEN,
1378                        data => '</' . $self->{s_kwd},
1379                        line => $self->{line_prev},
1380                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1381                       });
1382              redo A;
1383            }
1384          } else { # after "<{tag-name}"
1385            unless ($is_space->{$self->{nc}} or
1386                    {
1387                     0x003E => 1, # >
1388                     0x002F => 1, # /
1389                     -1 => 1, # EOF
1390                    }->{$self->{nc}}) {
1391              !!!cp (26);
1392              ## Reconsume.
1393              $self->{state} = DATA_STATE;
1394              !!!emit ({type => CHARACTER_TOKEN,
1395                        data => '</' . $self->{s_kwd},
1396                        line => $self->{line_prev},
1397                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1398                       });
1399              redo A;
1400            } else {
1401              !!!cp (27);
1402              $self->{ct}
1403                  = {type => END_TAG_TOKEN,
1404                     tag_name => $self->{last_stag_name},
1405                     line => $self->{line_prev},
1406                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1407              $self->{state} = TAG_NAME_STATE;
1408              ## Reconsume.
1409              redo A;
1410            }
1411        }        }
1412      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1413        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1414            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1415            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1416            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1417            $self->{next_input_character} == 0x0020) { # SP          redo A;
1418          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1419          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1420          redo A;            !!!cp (35);
1421        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1422          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {  
1423            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1424            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1425              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1426            }            #  !!! cp (36);
1427              #  !!! parse-error (type => 'end tag attribute');
1428              #} else {
1429                !!!cp (37);
1430              #}
1431          } else {          } else {
1432            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1433          }          }
1434          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1435          !!!next-input-character;          !!!next-input-character;
1436    
1437          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1438    
1439          redo A;          redo A;
1440        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1441                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1442          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1443            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1444            # start tag or end tag            # start tag or end tag
1445          ## Stay in this state          ## Stay in this state
1446          !!!next-input-character;          !!!next-input-character;
1447          redo A;          redo A;
1448        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1451            $self->{current_token}->{first_start_tag}            !!!cp (39);
1452                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1453            $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) {  
1454            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1456              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1457            }            #  !!! cp (40);
1458              #  !!! parse-error (type => 'end tag attribute');
1459              #} else {
1460                !!!cp (41);
1461              #}
1462          } else {          } else {
1463            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1464          }          }
1465          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1466          # reconsume          # reconsume
1467    
1468          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1469    
1470          redo A;          redo A;
1471        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1472            !!!cp (42);
1473            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1474          !!!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';  
         # next-input-character is already done  
1475          redo A;          redo A;
1476        } else {        } else {
1477          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1478            $self->{ct}->{tag_name} .= chr $self->{nc};
1479            # start tag or end tag            # start tag or end tag
1480          ## Stay in the state          ## Stay in the state
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        }        }
1484      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1485        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1486            $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  
1487          ## Stay in the state          ## Stay in the state
1488          !!!next-input-character;          !!!next-input-character;
1489          redo A;          redo A;
1490        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1491          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1492            $self->{current_token}->{first_start_tag}            !!!cp (46);
1493                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1494            $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) {  
1495            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1496            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1497                !!!cp (47);
1498              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1499              } else {
1500                !!!cp (48);
1501            }            }
1502          } else {          } else {
1503            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1504          }          }
1505          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1506          !!!next-input-character;          !!!next-input-character;
1507    
1508          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1509    
1510          redo A;          redo A;
1511        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1512                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1513          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1514                                value => ''};          $self->{ca}
1515          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1516                   value => '',
1517                   line => $self->{line}, column => $self->{column}};
1518            $self->{state} = ATTRIBUTE_NAME_STATE;
1519          !!!next-input-character;          !!!next-input-character;
1520          redo A;          redo A;
1521        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1522            !!!cp (50);
1523            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1524          !!!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  
1525          redo A;          redo A;
1526        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1527          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1528          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1529            $self->{current_token}->{first_start_tag}            !!!cp (52);
1530                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1531            $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) {  
1532            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1533            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1534                !!!cp (53);
1535              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1536              } else {
1537                !!!cp (54);
1538            }            }
1539          } else {          } else {
1540            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1541          }          }
1542          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1543          # reconsume          # reconsume
1544    
1545          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1546    
1547          redo A;          redo A;
1548        } else {        } else {
1549          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1550                                value => ''};               0x0022 => 1, # "
1551          $self->{state} = 'attribute name';               0x0027 => 1, # '
1552                 0x003D => 1, # =
1553                }->{$self->{nc}}) {
1554              !!!cp (55);
1555              !!!parse-error (type => 'bad attribute name');
1556            } else {
1557              !!!cp (56);
1558            }
1559            $self->{ca}
1560                = {name => chr ($self->{nc}),
1561                   value => '',
1562                   line => $self->{line}, column => $self->{column}};
1563            $self->{state} = ATTRIBUTE_NAME_STATE;
1564          !!!next-input-character;          !!!next-input-character;
1565          redo A;          redo A;
1566        }        }
1567      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1568        my $before_leave = sub {        my $before_leave = sub {
1569          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1570              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1571            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1572            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1573              ## Discard $self->{ca} # MUST
1574          } else {          } else {
1575            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            !!!cp (58);
1576              = $self->{current_attribute};            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1577                = $self->{ca};
1578          }          }
1579        }; # $before_leave        }; # $before_leave
1580    
1581        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1582            $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  
1583          $before_leave->();          $before_leave->();
1584          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1585          !!!next-input-character;          !!!next-input-character;
1586          redo A;          redo A;
1587        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1588            !!!cp (60);
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1594          $before_leave->();          $before_leave->();
1595          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1596            $self->{current_token}->{first_start_tag}            !!!cp (61);
1597                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1598            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1599          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {            !!!cp (62);
1600            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1601            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1602              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1603            }            }
1604          } else {          } else {
1605            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1606          }          }
1607          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1608          !!!next-input-character;          !!!next-input-character;
1609    
1610          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1611    
1612          redo A;          redo A;
1613        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1614                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1615          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1616            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1617          ## Stay in the state          ## Stay in the state
1618          !!!next-input-character;          !!!next-input-character;
1619          redo A;          redo A;
1620        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1621            !!!cp (64);
1622          $before_leave->();          $before_leave->();
1623            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1624          !!!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';  
         # next-input-character is already done  
1625          redo A;          redo A;
1626        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1627          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1628          $before_leave->();          $before_leave->();
1629          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1630            $self->{current_token}->{first_start_tag}            !!!cp (66);
1631                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1632            $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) {  
1633            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1634            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1635                !!!cp (67);
1636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1637              } else {
1638                ## NOTE: This state should never be reached.
1639                !!!cp (68);
1640            }            }
1641          } else {          } else {
1642            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1643          }          }
1644          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1645          # reconsume          # reconsume
1646    
1647          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1648    
1649          redo A;          redo A;
1650        } else {        } else {
1651          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1652                $self->{nc} == 0x0027) { # '
1653              !!!cp (69);
1654              !!!parse-error (type => 'bad attribute name');
1655            } else {
1656              !!!cp (70);
1657            }
1658            $self->{ca}->{name} .= chr ($self->{nc});
1659          ## Stay in the state          ## Stay in the state
1660          !!!next-input-character;          !!!next-input-character;
1661          redo A;          redo A;
1662        }        }
1663      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1664        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1665            $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  
1666          ## Stay in the state          ## Stay in the state
1667          !!!next-input-character;          !!!next-input-character;
1668          redo A;          redo A;
1669        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1670          $self->{state} = 'before attribute value';          !!!cp (72);
1671            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1672          !!!next-input-character;          !!!next-input-character;
1673          redo A;          redo A;
1674        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1675          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1676            $self->{current_token}->{first_start_tag}            !!!cp (73);
1677                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1678            $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) {  
1679            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1680            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1681                !!!cp (74);
1682              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1683              } else {
1684                ## NOTE: This state should never be reached.
1685                !!!cp (75);
1686            }            }
1687          } else {          } else {
1688            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1689          }          }
1690          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1691          !!!next-input-character;          !!!next-input-character;
1692    
1693          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1694    
1695          redo A;          redo A;
1696        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1697                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1698          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1699                                value => ''};          $self->{ca}
1700          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1701                   value => '',
1702                   line => $self->{line}, column => $self->{column}};
1703            $self->{state} = ATTRIBUTE_NAME_STATE;
1704          !!!next-input-character;          !!!next-input-character;
1705          redo A;          redo A;
1706        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1707            !!!cp (77);
1708            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1709          !!!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';  
         # next-input-character is already done  
1710          redo A;          redo A;
1711        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1712          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1713          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1714            $self->{current_token}->{first_start_tag}            !!!cp (79);
1715                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1716            $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) {  
1717            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1718            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1719                !!!cp (80);
1720              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1721              } else {
1722                ## NOTE: This state should never be reached.
1723                !!!cp (81);
1724            }            }
1725          } else {          } else {
1726            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1727          }          }
1728          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1729          # reconsume          # reconsume
1730    
1731          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1732    
1733          redo A;          redo A;
1734        } else {        } else {
1735          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1736                                value => ''};              $self->{nc} == 0x0027) { # '
1737          $self->{state} = 'attribute name';            !!!cp (78);
1738              !!!parse-error (type => 'bad attribute name');
1739            } else {
1740              !!!cp (82);
1741            }
1742            $self->{ca}
1743                = {name => chr ($self->{nc}),
1744                   value => '',
1745                   line => $self->{line}, column => $self->{column}};
1746            $self->{state} = ATTRIBUTE_NAME_STATE;
1747          !!!next-input-character;          !!!next-input-character;
1748          redo A;                  redo A;        
1749        }        }
1750      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1751        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1752            $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        
1753          ## Stay in the state          ## Stay in the state
1754          !!!next-input-character;          !!!next-input-character;
1755          redo A;          redo A;
1756        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1757          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1758            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1759          !!!next-input-character;          !!!next-input-character;
1760          redo A;          redo A;
1761        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1762          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1763            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1764          ## reconsume          ## reconsume
1765          redo A;          redo A;
1766        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1767          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1768            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1769          !!!next-input-character;          !!!next-input-character;
1770          redo A;          redo A;
1771        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1772          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!parse-error (type => 'empty unquoted attribute value');
1773            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1774                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1775            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1776          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1777            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1778            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1779                !!!cp (88);
1780              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1781              } else {
1782                ## NOTE: This state should never be reached.
1783                !!!cp (89);
1784            }            }
1785          } else {          } else {
1786            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1787          }          }
1788          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1789          !!!next-input-character;          !!!next-input-character;
1790    
1791          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1792    
1793          redo A;          redo A;
1794        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1795          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1796          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1797            $self->{current_token}->{first_start_tag}            !!!cp (90);
1798                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1799            $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) {  
1800            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1801            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1802                !!!cp (91);
1803              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1804              } else {
1805                ## NOTE: This state should never be reached.
1806                !!!cp (92);
1807            }            }
1808          } else {          } else {
1809            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1810          }          }
1811          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1812          ## reconsume          ## reconsume
1813    
1814          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1815    
1816          redo A;          redo A;
1817        } else {        } else {
1818          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1819          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1820              !!!parse-error (type => 'bad attribute value');
1821            } else {
1822              !!!cp (94);
1823            }
1824            $self->{ca}->{value} .= chr ($self->{nc});
1825            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1826          !!!next-input-character;          !!!next-input-character;
1827          redo A;          redo A;
1828        }        }
1829      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1830        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1831          $self->{state} = 'before attribute name';          !!!cp (95);
1832            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1833          !!!next-input-character;          !!!next-input-character;
1834          redo A;          redo A;
1835        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1836          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1837          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1838            ## "entity in attribute value state".  In this implementation, the
1839            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1840            ## implementation of the "consume a character reference" algorithm.
1841            $self->{prev_state} = $self->{state};
1842            $self->{entity_add} = 0x0022; # "
1843            $self->{state} = ENTITY_STATE;
1844          !!!next-input-character;          !!!next-input-character;
1845          redo A;          redo A;
1846        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1847          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1848          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1849            $self->{current_token}->{first_start_tag}            !!!cp (97);
1850                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1851            $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) {  
1852            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1853            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1854                !!!cp (98);
1855              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1856              } else {
1857                ## NOTE: This state should never be reached.
1858                !!!cp (99);
1859            }            }
1860          } else {          } else {
1861            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1862          }          }
1863          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1864          ## reconsume          ## reconsume
1865    
1866          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1867    
1868          redo A;          redo A;
1869        } else {        } else {
1870          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1871            $self->{ca}->{value} .= chr ($self->{nc});
1872            $self->{read_until}->($self->{ca}->{value},
1873                                  q["&],
1874                                  length $self->{ca}->{value});
1875    
1876          ## Stay in the state          ## Stay in the state
1877          !!!next-input-character;          !!!next-input-character;
1878          redo A;          redo A;
1879        }        }
1880      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1881        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1882          $self->{state} = 'before attribute name';          !!!cp (101);
1883            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1884            !!!next-input-character;
1885            redo A;
1886          } elsif ($self->{nc} == 0x0026) { # &
1887            !!!cp (102);
1888            ## NOTE: In the spec, the tokenizer is switched to the
1889            ## "entity in attribute value state".  In this implementation, the
1890            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1891            ## implementation of the "consume a character reference" algorithm.
1892            $self->{entity_add} = 0x0027; # '
1893            $self->{prev_state} = $self->{state};
1894            $self->{state} = ENTITY_STATE;
1895          !!!next-input-character;          !!!next-input-character;
1896          redo A;          redo A;
1897        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = 'attribute value (single-quoted)';  
         $self->{state} = 'entity in attribute value';  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1898          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1899          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1900            $self->{current_token}->{first_start_tag}            !!!cp (103);
1901                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1902            $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) {  
1903            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1904            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1905                !!!cp (104);
1906              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1907              } else {
1908                ## NOTE: This state should never be reached.
1909                !!!cp (105);
1910            }            }
1911          } else {          } else {
1912            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1913          }          }
1914          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1915          ## reconsume          ## reconsume
1916    
1917          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1918    
1919          redo A;          redo A;
1920        } else {        } else {
1921          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1922            $self->{ca}->{value} .= chr ($self->{nc});
1923            $self->{read_until}->($self->{ca}->{value},
1924                                  q['&],
1925                                  length $self->{ca}->{value});
1926    
1927          ## Stay in the state          ## Stay in the state
1928          !!!next-input-character;          !!!next-input-character;
1929          redo A;          redo A;
1930        }        }
1931      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1932        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1933            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1934            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1935            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1936            $self->{next_input_character} == 0x0020) { # SP          redo A;
1937          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1938          !!!next-input-character;          !!!cp (108);
1939          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1940        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1941          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1942          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1943          !!!next-input-character;          $self->{entity_add} = -1;
1944          redo A;          $self->{prev_state} = $self->{state};
1945        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1946          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          !!!next-input-character;
1947            $self->{current_token}->{first_start_tag}          redo A;
1948                = not defined $self->{last_emitted_start_tag_name};        } elsif ($self->{nc} == 0x003E) { # >
1949            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1950          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {            !!!cp (109);
1951              $self->{last_stag_name} = $self->{ct}->{tag_name};
1952            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1953            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1954            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1955                !!!cp (110);
1956              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1957              } else {
1958                ## NOTE: This state should never be reached.
1959                !!!cp (111);
1960            }            }
1961          } else {          } else {
1962            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1963          }          }
1964          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1965          !!!next-input-character;          !!!next-input-character;
1966    
1967          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1968    
1969          redo A;          redo A;
1970        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1971          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1972          if ($self->{current_token}->{type} == START_TAG_TOKEN) {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1973            $self->{current_token}->{first_start_tag}            !!!cp (112);
1974                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1975            $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) {  
1976            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1977            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1978                !!!cp (113);
1979              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1980              } else {
1981                ## NOTE: This state should never be reached.
1982                !!!cp (114);
1983            }            }
1984          } else {          } else {
1985            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1986          }          }
1987          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1988          ## reconsume          ## reconsume
1989    
1990          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1991    
1992          redo A;          redo A;
1993        } else {        } else {
1994          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1995                 0x0022 => 1, # "
1996                 0x0027 => 1, # '
1997                 0x003D => 1, # =
1998                }->{$self->{nc}}) {
1999              !!!cp (115);
2000              !!!parse-error (type => 'bad attribute value');
2001            } else {
2002              !!!cp (116);
2003            }
2004            $self->{ca}->{value} .= chr ($self->{nc});
2005            $self->{read_until}->($self->{ca}->{value},
2006                                  q["'=& >],
2007                                  length $self->{ca}->{value});
2008    
2009          ## Stay in the state          ## Stay in the state
2010          !!!next-input-character;          !!!next-input-character;
2011          redo A;          redo A;
2012        }        }
2013      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2014        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2015            !!!cp (118);
2016            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2017            !!!next-input-character;
2018            redo A;
2019          } elsif ($self->{nc} == 0x003E) { # >
2020            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2021              !!!cp (119);
2022              $self->{last_stag_name} = $self->{ct}->{tag_name};
2023            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2024              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2025              if ($self->{ct}->{attributes}) {
2026                !!!cp (120);
2027                !!!parse-error (type => 'end tag attribute');
2028              } else {
2029                ## NOTE: This state should never be reached.
2030                !!!cp (121);
2031              }
2032            } else {
2033              die "$0: $self->{ct}->{type}: Unknown token type";
2034            }
2035            $self->{state} = DATA_STATE;
2036            !!!next-input-character;
2037    
2038            !!!emit ($self->{ct}); # start tag or end tag
2039    
2040        unless (defined $token) {          redo A;
2041          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2042            !!!cp (122);
2043            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2044            !!!next-input-character;
2045            redo A;
2046          } elsif ($self->{nc} == -1) {
2047            !!!parse-error (type => 'unclosed tag');
2048            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2049              !!!cp (122.3);
2050              $self->{last_stag_name} = $self->{ct}->{tag_name};
2051            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2052              if ($self->{ct}->{attributes}) {
2053                !!!cp (122.1);
2054                !!!parse-error (type => 'end tag attribute');
2055              } else {
2056                ## NOTE: This state should never be reached.
2057                !!!cp (122.2);
2058              }
2059            } else {
2060              die "$0: $self->{ct}->{type}: Unknown token type";
2061            }
2062            $self->{state} = DATA_STATE;
2063            ## Reconsume.
2064            !!!emit ($self->{ct}); # start tag or end tag
2065            redo A;
2066        } else {        } else {
2067          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2068          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2069            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2070            ## reconsume
2071            redo A;
2072        }        }
2073        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2074          if ($self->{nc} == 0x003E) { # >
2075            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2076              !!!cp ('124.2');
2077              !!!parse-error (type => 'nestc', token => $self->{ct});
2078              ## TODO: Different type than slash in start tag
2079              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2080              if ($self->{ct}->{attributes}) {
2081                !!!cp ('124.4');
2082                !!!parse-error (type => 'end tag attribute');
2083              } else {
2084                !!!cp ('124.5');
2085              }
2086              ## TODO: Test |<title></title/>|
2087            } else {
2088              !!!cp ('124.3');
2089              $self->{self_closing} = 1;
2090            }
2091    
2092        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2093        # next-input-character is already done          !!!next-input-character;
       redo A;  
     } elsif ($self->{state} eq 'bogus comment') {  
       ## (only happen if PCDATA state)  
         
       my $token = {type => COMMENT_TOKEN, data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
   
           !!!emit ($token);  
   
           redo A;  
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2094    
2095            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2096    
2097            redo A;          redo A;
2098          } elsif ($self->{nc} == -1) {
2099            !!!parse-error (type => 'unclosed tag');
2100            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2101              !!!cp (124.7);
2102              $self->{last_stag_name} = $self->{ct}->{tag_name};
2103            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2104              if ($self->{ct}->{attributes}) {
2105                !!!cp (124.5);
2106                !!!parse-error (type => 'end tag attribute');
2107              } else {
2108                ## NOTE: This state should never be reached.
2109                !!!cp (124.6);
2110              }
2111          } else {          } else {
2112            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2113          }          }
2114        } # BC          $self->{state} = DATA_STATE;
2115      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2116            !!!emit ($self->{ct}); # start tag or end tag
2117            redo A;
2118          } else {
2119            !!!cp ('124.4');
2120            !!!parse-error (type => 'nestc');
2121            ## TODO: This error type is wrong.
2122            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2123            ## Reconsume.
2124            redo A;
2125          }
2126        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2127        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2128    
2129        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2130        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2131                
2132        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2133            !!!cp (124);
2134            $self->{state} = DATA_STATE;
2135          !!!next-input-character;          !!!next-input-character;
2136          push @next_char, $self->{next_input_character};  
2137          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2138            $self->{current_token} = {type => COMMENT_TOKEN, data => ''};          redo A;
2139            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2140            !!!next-input-character;          !!!cp (125);
2141            redo A;          $self->{state} = DATA_STATE;
2142          }          ## reconsume
2143        } elsif ($self->{next_input_character} == 0x0044 or # D  
2144                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2145            redo A;
2146          } else {
2147            !!!cp (126);
2148            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2149            $self->{read_until}->($self->{ct}->{data},
2150                                  q[>],
2151                                  length $self->{ct}->{data});
2152    
2153            ## Stay in the state.
2154          !!!next-input-character;          !!!next-input-character;
2155          push @next_char, $self->{next_input_character};          redo A;
2156          if ($self->{next_input_character} == 0x004F or # O        }
2157              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2158            !!!next-input-character;        ## (only happen if PCDATA state)
2159            push @next_char, $self->{next_input_character};        
2160            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2161                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2162              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2163              push @next_char, $self->{next_input_character};          !!!next-input-character;
2164              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2165                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2166                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2167                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2168                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2169                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2170                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2171                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2172                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2173                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2174                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2175                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2176                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2177                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2178                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2179                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2180                      !!!next-input-character;          redo A;
2181                      redo A;        } else {
2182                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2183        }        }
2184    
2185        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2186        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2187        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2188        $self->{state} = 'bogus comment';        ## Reconsume.
2189          $self->{state} = BOGUS_COMMENT_STATE;
2190          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                    line => $self->{line_prev},
2192                                    column => $self->{column_prev} - 1,
2193                                   };
2194        redo A;        redo A;
2195              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2196        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2197        ## 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);
2198      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2199        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2200          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2201                                     };
2202            $self->{state} = COMMENT_START_STATE;
2203            !!!next-input-character;
2204            redo A;
2205          } else {
2206            !!!cp (128);
2207            !!!parse-error (type => 'bogus comment',
2208                            line => $self->{line_prev},
2209                            column => $self->{column_prev} - 2);
2210            $self->{state} = BOGUS_COMMENT_STATE;
2211            ## Reconsume.
2212            $self->{ct} = {type => COMMENT_TOKEN,
2213                                      data => '-',
2214                                      line => $self->{line_prev},
2215                                      column => $self->{column_prev} - 2,
2216                                     };
2217            redo A;
2218          }
2219        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2220          ## ASCII case-insensitive.
2221          if ($self->{nc} == [
2222                undef,
2223                0x004F, # O
2224                0x0043, # C
2225                0x0054, # T
2226                0x0059, # Y
2227                0x0050, # P
2228              ]->[length $self->{s_kwd}] or
2229              $self->{nc} == [
2230                undef,
2231                0x006F, # o
2232                0x0063, # c
2233                0x0074, # t
2234                0x0079, # y
2235                0x0070, # p
2236              ]->[length $self->{s_kwd}]) {
2237            !!!cp (131);
2238            ## Stay in the state.
2239            $self->{s_kwd} .= chr $self->{nc};
2240            !!!next-input-character;
2241            redo A;
2242          } elsif ((length $self->{s_kwd}) == 6 and
2243                   ($self->{nc} == 0x0045 or # E
2244                    $self->{nc} == 0x0065)) { # e
2245            !!!cp (129);
2246            $self->{state} = DOCTYPE_STATE;
2247            $self->{ct} = {type => DOCTYPE_TOKEN,
2248                                      quirks => 1,
2249                                      line => $self->{line_prev},
2250                                      column => $self->{column_prev} - 7,
2251                                     };
2252            !!!next-input-character;
2253            redo A;
2254          } else {
2255            !!!cp (132);        
2256            !!!parse-error (type => 'bogus comment',
2257                            line => $self->{line_prev},
2258                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2259            $self->{state} = BOGUS_COMMENT_STATE;
2260            ## Reconsume.
2261            $self->{ct} = {type => COMMENT_TOKEN,
2262                                      data => $self->{s_kwd},
2263                                      line => $self->{line_prev},
2264                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2265                                     };
2266            redo A;
2267          }
2268        } elsif ($self->{state} == MD_CDATA_STATE) {
2269          if ($self->{nc} == {
2270                '[' => 0x0043, # C
2271                '[C' => 0x0044, # D
2272                '[CD' => 0x0041, # A
2273                '[CDA' => 0x0054, # T
2274                '[CDAT' => 0x0041, # A
2275              }->{$self->{s_kwd}}) {
2276            !!!cp (135.1);
2277            ## Stay in the state.
2278            $self->{s_kwd} .= chr $self->{nc};
2279            !!!next-input-character;
2280            redo A;
2281          } elsif ($self->{s_kwd} eq '[CDATA' and
2282                   $self->{nc} == 0x005B) { # [
2283            !!!cp (135.2);
2284            $self->{ct} = {type => CHARACTER_TOKEN,
2285                                      data => '',
2286                                      line => $self->{line_prev},
2287                                      column => $self->{column_prev} - 7};
2288            $self->{state} = CDATA_SECTION_STATE;
2289            !!!next-input-character;
2290            redo A;
2291          } else {
2292            !!!cp (135.3);
2293            !!!parse-error (type => 'bogus comment',
2294                            line => $self->{line_prev},
2295                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2296            $self->{state} = BOGUS_COMMENT_STATE;
2297            ## Reconsume.
2298            $self->{ct} = {type => COMMENT_TOKEN,
2299                                      data => $self->{s_kwd},
2300                                      line => $self->{line_prev},
2301                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2302                                     };
2303            redo A;
2304          }
2305        } elsif ($self->{state} == COMMENT_START_STATE) {
2306          if ($self->{nc} == 0x002D) { # -
2307            !!!cp (137);
2308            $self->{state} = COMMENT_START_DASH_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2312            !!!cp (138);
2313          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2314          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316    
2317          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2318    
2319          redo A;          redo A;
2320        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2321            !!!cp (139);
2322          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2323          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2324          ## reconsume          ## reconsume
2325    
2326          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2327    
2328          redo A;          redo A;
2329        } else {        } else {
2330          $self->{current_token}->{data} # comment          !!!cp (140);
2331              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2332          $self->{state} = 'comment';              .= chr ($self->{nc});
2333            $self->{state} = COMMENT_STATE;
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        }        }
2337      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2338        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2339          $self->{state} = 'comment end';          !!!cp (141);
2340            $self->{state} = COMMENT_END_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2344            !!!cp (142);
2345          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2346          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2353            !!!cp (143);
2354          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2355          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2356          ## reconsume          ## reconsume
2357    
2358          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2359    
2360          redo A;          redo A;
2361        } else {        } else {
2362          $self->{current_token}->{data} # comment          !!!cp (144);
2363              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2364          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2365            $self->{state} = COMMENT_STATE;
2366          !!!next-input-character;          !!!next-input-character;
2367          redo A;          redo A;
2368        }        }
2369      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2370        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2371          $self->{state} = 'comment end dash';          !!!cp (145);
2372            $self->{state} = COMMENT_END_DASH_STATE;
2373          !!!next-input-character;          !!!next-input-character;
2374          redo A;          redo A;
2375        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2376            !!!cp (146);
2377          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2378          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2379          ## reconsume          ## reconsume
2380    
2381          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2382    
2383          redo A;          redo A;
2384        } else {        } else {
2385          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2386            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2387            $self->{read_until}->($self->{ct}->{data},
2388                                  q[-],
2389                                  length $self->{ct}->{data});
2390    
2391          ## Stay in the state          ## Stay in the state
2392          !!!next-input-character;          !!!next-input-character;
2393          redo A;          redo A;
2394        }        }
2395      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2396        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2397          $self->{state} = 'comment end';          !!!cp (148);
2398            $self->{state} = COMMENT_END_STATE;
2399          !!!next-input-character;          !!!next-input-character;
2400          redo A;          redo A;
2401        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2402            !!!cp (149);
2403          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2404          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2405          ## reconsume          ## reconsume
2406    
2407          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2408    
2409          redo A;          redo A;
2410        } else {        } else {
2411          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2412          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2413            $self->{state} = COMMENT_STATE;
2414          !!!next-input-character;          !!!next-input-character;
2415          redo A;          redo A;
2416        }        }
2417      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2418        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2419          $self->{state} = 'data';          !!!cp (151);
2420            $self->{state} = DATA_STATE;
2421          !!!next-input-character;          !!!next-input-character;
2422    
2423          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2424    
2425          redo A;          redo A;
2426        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2427          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2428          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2429                            line => $self->{line_prev},
2430                            column => $self->{column_prev});
2431            $self->{ct}->{data} .= '-'; # comment
2432          ## Stay in the state          ## Stay in the state
2433          !!!next-input-character;          !!!next-input-character;
2434          redo A;          redo A;
2435        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2436            !!!cp (153);
2437          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2438          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2439          ## reconsume          ## reconsume
2440    
2441          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2442    
2443          redo A;          redo A;
2444        } else {        } else {
2445          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2446          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2447          $self->{state} = 'comment';                          line => $self->{line_prev},
2448                            column => $self->{column_prev});
2449            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2450            $self->{state} = COMMENT_STATE;
2451          !!!next-input-character;          !!!next-input-character;
2452          redo A;          redo A;
2453        }        }
2454      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2455        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2456            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2457            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'before DOCTYPE name';  
2458          !!!next-input-character;          !!!next-input-character;
2459          redo A;          redo A;
2460        } else {        } else {
2461            !!!cp (156);
2462          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2463          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2464          ## reconsume          ## reconsume
2465          redo A;          redo A;
2466        }        }
2467      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2468        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2469            $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  
2470          ## Stay in the state          ## Stay in the state
2471          !!!next-input-character;          !!!next-input-character;
2472          redo A;          redo A;
2473        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2474            !!!cp (158);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2477          !!!next-input-character;          !!!next-input-character;
2478    
2479          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2483            !!!cp (159);
2484          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2485          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2486          ## reconsume          ## reconsume
2487    
2488          !!!emit ({type => DOCTYPE_TOKEN}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2489    
2490          redo A;          redo A;
2491        } else {        } else {
2492          $self->{current_token}          !!!cp (160);
2493              = {type => DOCTYPE_TOKEN,          $self->{ct}->{name} = chr $self->{nc};
2494                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
2495                 correct => 1};          $self->{state} = DOCTYPE_NAME_STATE;
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = 'DOCTYPE name';  
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        }        }
2499      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2500  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2501        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2502            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2503            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'after DOCTYPE name';  
2504          !!!next-input-character;          !!!next-input-character;
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2507          $self->{state} = 'data';          !!!cp (162);
2508            $self->{state} = DATA_STATE;
2509          !!!next-input-character;          !!!next-input-character;
2510    
2511          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2512    
2513          redo A;          redo A;
2514        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2515            !!!cp (163);
2516          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2517          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2518          ## reconsume          ## reconsume
2519    
2520          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2521          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2522    
2523          redo A;          redo A;
2524        } else {        } else {
2525          $self->{current_token}->{name}          !!!cp (164);
2526            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2527              .= chr ($self->{nc}); # DOCTYPE
2528          ## Stay in the state          ## Stay in the state
2529          !!!next-input-character;          !!!next-input-character;
2530          redo A;          redo A;
2531        }        }
2532      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2533        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2534            $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  
2535          ## Stay in the state          ## Stay in the state
2536          !!!next-input-character;          !!!next-input-character;
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2539          $self->{state} = 'data';          !!!cp (166);
2540            $self->{state} = DATA_STATE;
2541          !!!next-input-character;          !!!next-input-character;
2542    
2543          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2544    
2545          redo A;          redo A;
2546        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2547            !!!cp (167);
2548          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2549          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2550          ## reconsume          ## reconsume
2551    
2552          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2553          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2554    
2555          redo A;          redo A;
2556        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2557                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2558            $self->{state} = PUBLIC_STATE;
2559            $self->{s_kwd} = chr $self->{nc};
2560          !!!next-input-character;          !!!next-input-character;
2561          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2562              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2563            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2564            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2565                $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';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2566          !!!next-input-character;          !!!next-input-character;
2567          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';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2568        } else {        } else {
2569            !!!cp (180);
2570            !!!parse-error (type => 'string after DOCTYPE name');
2571            $self->{ct}->{quirks} = 1;
2572    
2573            $self->{state} = BOGUS_DOCTYPE_STATE;
2574          !!!next-input-character;          !!!next-input-character;
2575          #          redo A;
2576        }        }
2577        } elsif ($self->{state} == PUBLIC_STATE) {
2578          ## ASCII case-insensitive
2579          if ($self->{nc} == [
2580                undef,
2581                0x0055, # U
2582                0x0042, # B
2583                0x004C, # L
2584                0x0049, # I
2585              ]->[length $self->{s_kwd}] or
2586              $self->{nc} == [
2587                undef,
2588                0x0075, # u
2589                0x0062, # b
2590                0x006C, # l
2591                0x0069, # i
2592              ]->[length $self->{s_kwd}]) {
2593            !!!cp (175);
2594            ## Stay in the state.
2595            $self->{s_kwd} .= chr $self->{nc};
2596            !!!next-input-character;
2597            redo A;
2598          } elsif ((length $self->{s_kwd}) == 5 and
2599                   ($self->{nc} == 0x0043 or # C
2600                    $self->{nc} == 0x0063)) { # c
2601            !!!cp (168);
2602            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2603            !!!next-input-character;
2604            redo A;
2605          } else {
2606            !!!cp (169);
2607            !!!parse-error (type => 'string after DOCTYPE name',
2608                            line => $self->{line_prev},
2609                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2610            $self->{ct}->{quirks} = 1;
2611    
2612        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2613        $self->{state} = 'bogus DOCTYPE';          ## Reconsume.
2614        # next-input-character is already done          redo A;
2615        redo A;        }
2616      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == SYSTEM_STATE) {
2617        if ({        ## ASCII case-insensitive
2618              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,        if ($self->{nc} == [
2619              #0x000D => 1, # HT, LF, VT, FF, SP, CR              undef,
2620            }->{$self->{next_input_character}}) {              0x0059, # Y
2621                0x0053, # S
2622                0x0054, # T
2623                0x0045, # E
2624              ]->[length $self->{s_kwd}] or
2625              $self->{nc} == [
2626                undef,
2627                0x0079, # y
2628                0x0073, # s
2629                0x0074, # t
2630                0x0065, # e
2631              ]->[length $self->{s_kwd}]) {
2632            !!!cp (170);
2633            ## Stay in the state.
2634            $self->{s_kwd} .= chr $self->{nc};
2635            !!!next-input-character;
2636            redo A;
2637          } elsif ((length $self->{s_kwd}) == 5 and
2638                   ($self->{nc} == 0x004D or # M
2639                    $self->{nc} == 0x006D)) { # m
2640            !!!cp (171);
2641            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2642            !!!next-input-character;
2643            redo A;
2644          } else {
2645            !!!cp (172);
2646            !!!parse-error (type => 'string after DOCTYPE name',
2647                            line => $self->{line_prev},
2648                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2649            $self->{ct}->{quirks} = 1;
2650    
2651            $self->{state} = BOGUS_DOCTYPE_STATE;
2652            ## Reconsume.
2653            redo A;
2654          }
2655        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2656          if ($is_space->{$self->{nc}}) {
2657            !!!cp (181);
2658          ## Stay in the state          ## Stay in the state
2659          !!!next-input-character;          !!!next-input-character;
2660          redo A;          redo A;
2661        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2662          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2663          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2664            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2668          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2669          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2670            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2674            !!!cp (184);
2675          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2676    
2677          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2678          !!!next-input-character;          !!!next-input-character;
2679    
2680          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2681          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2682    
2683          redo A;          redo A;
2684        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2685            !!!cp (185);
2686          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2687    
2688          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2689          ## reconsume          ## reconsume
2690    
2691          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2692          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2693    
2694          redo A;          redo A;
2695        } else {        } else {
2696            !!!cp (186);
2697          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2698          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2699    
2700            $self->{state} = BOGUS_DOCTYPE_STATE;
2701          !!!next-input-character;          !!!next-input-character;
2702          redo A;          redo A;
2703        }        }
2704      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2705        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2706          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2707            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2708          !!!next-input-character;          !!!next-input-character;
2709          redo A;          redo A;
2710        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2711            !!!cp (188);
2712            !!!parse-error (type => 'unclosed PUBLIC literal');
2713    
2714            $self->{state} = DATA_STATE;
2715            !!!next-input-character;
2716    
2717            $self->{ct}->{quirks} = 1;
2718            !!!emit ($self->{ct}); # DOCTYPE
2719    
2720            redo A;
2721          } elsif ($self->{nc} == -1) {
2722            !!!cp (189);
2723          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2724    
2725          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2726          ## reconsume          ## reconsume
2727    
2728          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2729          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2730    
2731          redo A;          redo A;
2732        } else {        } else {
2733          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2734              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2735                .= chr $self->{nc};
2736            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2737                                  length $self->{ct}->{pubid});
2738    
2739          ## Stay in the state          ## Stay in the state
2740          !!!next-input-character;          !!!next-input-character;
2741          redo A;          redo A;
2742        }        }
2743      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2744        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2745          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2746            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2747          !!!next-input-character;          !!!next-input-character;
2748          redo A;          redo A;
2749        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2750            !!!cp (192);
2751            !!!parse-error (type => 'unclosed PUBLIC literal');
2752    
2753            $self->{state} = DATA_STATE;
2754            !!!next-input-character;
2755    
2756            $self->{ct}->{quirks} = 1;
2757            !!!emit ($self->{ct}); # DOCTYPE
2758    
2759            redo A;
2760          } elsif ($self->{nc} == -1) {
2761            !!!cp (193);
2762          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2763    
2764          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2765          ## reconsume          ## reconsume
2766    
2767          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2768          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2769    
2770          redo A;          redo A;
2771        } else {        } else {
2772          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2773              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2774                .= chr $self->{nc};
2775            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2776                                  length $self->{ct}->{pubid});
2777    
2778          ## Stay in the state          ## Stay in the state
2779          !!!next-input-character;          !!!next-input-character;
2780          redo A;          redo A;
2781        }        }
2782      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2783        if ({        if ($is_space->{$self->{nc}}) {
2784              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2785          ## Stay in the state          ## Stay in the state
2786          !!!next-input-character;          !!!next-input-character;
2787          redo A;          redo A;
2788        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2789          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2790          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2791            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2795          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2796          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2797            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2801          $self->{state} = 'data';          !!!cp (198);
2802            $self->{state} = DATA_STATE;
2803          !!!next-input-character;          !!!next-input-character;
2804    
2805          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2806    
2807          redo A;          redo A;
2808        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2809            !!!cp (199);
2810          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2811    
2812          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2813          ## reconsume          ## reconsume
2814    
2815          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2816          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2817    
2818          redo A;          redo A;
2819        } else {        } else {
2820            !!!cp (200);
2821          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2822          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2823    
2824            $self->{state} = BOGUS_DOCTYPE_STATE;
2825          !!!next-input-character;          !!!next-input-character;
2826          redo A;          redo A;
2827        }        }
2828      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2829        if ({        if ($is_space->{$self->{nc}}) {
2830              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2831          ## Stay in the state          ## Stay in the state
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2835          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2836          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2837            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2841          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2842          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2843            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2847            !!!cp (204);
2848          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2849          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851    
2852          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2853          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2854    
2855          redo A;          redo A;
2856        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2857            !!!cp (205);
2858          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2859    
2860          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2861          ## reconsume          ## reconsume
2862    
2863          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2864          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2865    
2866          redo A;          redo A;
2867        } else {        } else {
2868            !!!cp (206);
2869          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2870          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2871    
2872            $self->{state} = BOGUS_DOCTYPE_STATE;
2873          !!!next-input-character;          !!!next-input-character;
2874          redo A;          redo A;
2875        }        }
2876      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2877        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2878          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2879            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2880            !!!next-input-character;
2881            redo A;
2882          } elsif ($self->{nc} == 0x003E) { # >
2883            !!!cp (208);
2884            !!!parse-error (type => 'unclosed SYSTEM literal');
2885    
2886            $self->{state} = DATA_STATE;
2887          !!!next-input-character;          !!!next-input-character;
2888    
2889            $self->{ct}->{quirks} = 1;
2890            !!!emit ($self->{ct}); # DOCTYPE
2891    
2892          redo A;          redo A;
2893        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2894            !!!cp (209);
2895          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2896    
2897          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2898          ## reconsume          ## reconsume
2899    
2900          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2901          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2902    
2903          redo A;          redo A;
2904        } else {        } else {
2905          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2906              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2907                .= chr $self->{nc};
2908            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2909                                  length $self->{ct}->{sysid});
2910    
2911          ## Stay in the state          ## Stay in the state
2912          !!!next-input-character;          !!!next-input-character;
2913          redo A;          redo A;
2914        }        }
2915      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2916        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2917          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2918            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2919            !!!next-input-character;
2920            redo A;
2921          } elsif ($self->{nc} == 0x003E) { # >
2922            !!!cp (212);
2923            !!!parse-error (type => 'unclosed SYSTEM literal');
2924    
2925            $self->{state} = DATA_STATE;
2926          !!!next-input-character;          !!!next-input-character;
2927    
2928            $self->{ct}->{quirks} = 1;
2929            !!!emit ($self->{ct}); # DOCTYPE
2930    
2931          redo A;          redo A;
2932        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2933            !!!cp (213);
2934          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2935    
2936          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2937          ## reconsume          ## reconsume
2938    
2939          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2940          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2941    
2942          redo A;          redo A;
2943        } else {        } else {
2944          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2945              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2946                .= chr $self->{nc};
2947            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2948                                  length $self->{ct}->{sysid});
2949    
2950          ## Stay in the state          ## Stay in the state
2951          !!!next-input-character;          !!!next-input-character;
2952          redo A;          redo A;
2953        }        }
2954      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2955        if ({        if ($is_space->{$self->{nc}}) {
2956              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2957          ## Stay in the state          ## Stay in the state
2958          !!!next-input-character;          !!!next-input-character;
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2961          $self->{state} = 'data';          !!!cp (216);
2962            $self->{state} = DATA_STATE;
2963          !!!next-input-character;          !!!next-input-character;
2964    
2965          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2966    
2967          redo A;          redo A;
2968        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2969            !!!cp (217);
2970          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2971            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2972          ## reconsume          ## reconsume
2973    
2974          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2975          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2976    
2977          redo A;          redo A;
2978        } else {        } else {
2979            !!!cp (218);
2980          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2981          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2982    
2983            $self->{state} = BOGUS_DOCTYPE_STATE;
2984          !!!next-input-character;          !!!next-input-character;
2985          redo A;          redo A;
2986        }        }
2987      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2988        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2989          $self->{state} = 'data';          !!!cp (219);
2990            $self->{state} = DATA_STATE;
2991          !!!next-input-character;          !!!next-input-character;
2992    
2993          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2994    
2995          redo A;          redo A;
2996        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2997          !!!parse-error (type => 'unclosed DOCTYPE');          !!!cp (220);
2998          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2999          ## reconsume          ## reconsume
3000    
3001          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3002    
3003          redo A;          redo A;
3004        } else {        } else {
3005            !!!cp (221);
3006            my $s = '';
3007            $self->{read_until}->($s, q[>], 0);
3008    
3009          ## Stay in the state          ## Stay in the state
3010          !!!next-input-character;          !!!next-input-character;
3011          redo A;          redo A;
3012        }        }
3013      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3014        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3015      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3016    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3017          
3018    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3019  } # _get_next_token          !!!cp (221.1);
3020            $self->{state} = CDATA_SECTION_MSE1_STATE;
3021            !!!next-input-character;
3022            redo A;
3023          } elsif ($self->{nc} == -1) {
3024            $self->{state} = DATA_STATE;
3025            !!!next-input-character;
3026            if (length $self->{ct}->{data}) { # character
3027              !!!cp (221.2);
3028              !!!emit ($self->{ct}); # character
3029            } else {
3030              !!!cp (221.3);
3031              ## No token to emit. $self->{ct} is discarded.
3032            }        
3033            redo A;
3034          } else {
3035            !!!cp (221.4);
3036            $self->{ct}->{data} .= chr $self->{nc};
3037            $self->{read_until}->($self->{ct}->{data},
3038                                  q<]>,
3039                                  length $self->{ct}->{data});
3040    
3041  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3042    my ($self, $in_attr) = @_;          !!!next-input-character;
3043            redo A;
3044          }
3045    
3046    if ({        ## ISSUE: "text tokens" in spec.
3047         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3048         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3049        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3050      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3051      ## No error          !!!next-input-character;
3052      return undef;          redo A;
3053    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3054      !!!next-input-character;          !!!cp (221.6);
3055      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3056          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3057        my $code;          ## Reconsume.
3058        X: {          redo A;
3059          my $x_char = $self->{next_input_character};        }
3060          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3061          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3062              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3063            $code ||= 0;          !!!next-input-character;
3064            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3065            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3066            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3067          } else {          } else {
3068            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3069              ## No token to emit. $self->{ct} is discarded.
3070          }          }
3071            redo A;
3072          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3073            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3074            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3075          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3076            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3077            $code = 0xFFFD;          redo A;
3078          } elsif ($code == 0x000D) {        } else {
3079            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3080            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3081          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3082            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3083            $code = $c1_entity_char->{$code};          redo A;
3084          }        }
3085        } elsif ($self->{state} == ENTITY_STATE) {
3086          return {type => CHARACTER_TOKEN, data => chr $code};        if ($is_space->{$self->{nc}} or
3087        } # X            {
3088      } elsif (0x0030 <= $self->{next_input_character} and              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3089               $self->{next_input_character} <= 0x0039) { # 0..9              $self->{entity_add} => 1,
3090        my $code = $self->{next_input_character} - 0x0030;            }->{$self->{nc}}) {
3091        !!!next-input-character;          !!!cp (1001);
3092                  ## Don't consume
3093        while (0x0030 <= $self->{next_input_character} and          ## No error
3094                  $self->{next_input_character} <= 0x0039) { # 0..9          ## Return nothing.
3095          $code *= 10;          #
3096          $code += $self->{next_input_character} - 0x0030;        } elsif ($self->{nc} == 0x0023) { # #
3097                    !!!cp (999);
3098            $self->{state} = ENTITY_HASH_STATE;
3099            $self->{s_kwd} = '#';
3100            !!!next-input-character;
3101            redo A;
3102          } elsif ((0x0041 <= $self->{nc} and
3103                    $self->{nc} <= 0x005A) or # A..Z
3104                   (0x0061 <= $self->{nc} and
3105                    $self->{nc} <= 0x007A)) { # a..z
3106            !!!cp (998);
3107            require Whatpm::_NamedEntityList;
3108            $self->{state} = ENTITY_NAME_STATE;
3109            $self->{s_kwd} = chr $self->{nc};
3110            $self->{entity__value} = $self->{s_kwd};
3111            $self->{entity__match} = 0;
3112          !!!next-input-character;          !!!next-input-character;
3113            redo A;
3114          } else {
3115            !!!cp (1027);
3116            !!!parse-error (type => 'bare ero');
3117            ## Return nothing.
3118            #
3119        }        }
3120    
3121        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3122          ## reference" algorithm.  In other word, there is an "&" character
3123          ## that does not introduce a character reference, which would be
3124          ## appended to the parent element or the attribute value in later
3125          ## process of the tokenizer.
3126    
3127          if ($self->{prev_state} == DATA_STATE) {
3128            !!!cp (997);
3129            $self->{state} = $self->{prev_state};
3130            ## Reconsume.
3131            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3132                      line => $self->{line_prev},
3133                      column => $self->{column_prev},
3134                     });
3135            redo A;
3136          } else {
3137            !!!cp (996);
3138            $self->{ca}->{value} .= '&';
3139            $self->{state} = $self->{prev_state};
3140            ## Reconsume.
3141            redo A;
3142          }
3143        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3144          if ($self->{nc} == 0x0078 or # x
3145              $self->{nc} == 0x0058) { # X
3146            !!!cp (995);
3147            $self->{state} = HEXREF_X_STATE;
3148            $self->{s_kwd} .= chr $self->{nc};
3149            !!!next-input-character;
3150            redo A;
3151          } elsif (0x0030 <= $self->{nc} and
3152                   $self->{nc} <= 0x0039) { # 0..9
3153            !!!cp (994);
3154            $self->{state} = NCR_NUM_STATE;
3155            $self->{s_kwd} = $self->{nc} - 0x0030;
3156          !!!next-input-character;          !!!next-input-character;
3157            redo A;
3158        } else {        } else {
3159            !!!parse-error (type => 'bare nero',
3160                            line => $self->{line_prev},
3161                            column => $self->{column_prev} - 1);
3162    
3163            ## NOTE: According to the spec algorithm, nothing is returned,
3164            ## and then "&#" is appended to the parent element or the attribute
3165            ## value in the later processing.
3166    
3167            if ($self->{prev_state} == DATA_STATE) {
3168              !!!cp (1019);
3169              $self->{state} = $self->{prev_state};
3170              ## Reconsume.
3171              !!!emit ({type => CHARACTER_TOKEN,
3172                        data => '&#',
3173                        line => $self->{line_prev},
3174                        column => $self->{column_prev} - 1,
3175                       });
3176              redo A;
3177            } else {
3178              !!!cp (993);
3179              $self->{ca}->{value} .= '&#';
3180              $self->{state} = $self->{prev_state};
3181              ## Reconsume.
3182              redo A;
3183            }
3184          }
3185        } elsif ($self->{state} == NCR_NUM_STATE) {
3186          if (0x0030 <= $self->{nc} and
3187              $self->{nc} <= 0x0039) { # 0..9
3188            !!!cp (1012);
3189            $self->{s_kwd} *= 10;
3190            $self->{s_kwd} += $self->{nc} - 0x0030;
3191            
3192            ## Stay in the state.
3193            !!!next-input-character;
3194            redo A;
3195          } elsif ($self->{nc} == 0x003B) { # ;
3196            !!!cp (1013);
3197            !!!next-input-character;
3198            #
3199          } else {
3200            !!!cp (1014);
3201          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3202            ## Reconsume.
3203            #
3204        }        }
3205    
3206        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3207          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3208          my $c = $self->{column_prev};
3209          if ($charref_map->{$code}) {
3210            !!!cp (1015);
3211            !!!parse-error (type => 'invalid character reference',
3212                            text => (sprintf 'U+%04X', $code),
3213                            line => $l, column => $c);
3214            $code = $charref_map->{$code};
3215          } elsif ($code > 0x10FFFF) {
3216            !!!cp (1016);
3217            !!!parse-error (type => 'invalid character reference',
3218                            text => (sprintf 'U-%08X', $code),
3219                            line => $l, column => $c);
3220          $code = 0xFFFD;          $code = 0xFFFD;
3221          }
3222    
3223          if ($self->{prev_state} == DATA_STATE) {
3224            !!!cp (992);
3225            $self->{state} = $self->{prev_state};
3226            ## Reconsume.
3227            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3228                      line => $l, column => $c,
3229                     });
3230            redo A;
3231          } else {
3232            !!!cp (991);
3233            $self->{ca}->{value} .= chr $code;
3234            $self->{ca}->{has_reference} = 1;
3235            $self->{state} = $self->{prev_state};
3236            ## Reconsume.
3237            redo A;
3238          }
3239        } elsif ($self->{state} == HEXREF_X_STATE) {
3240          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3241              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3242              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3243            # 0..9, A..F, a..f
3244            !!!cp (990);
3245            $self->{state} = HEXREF_HEX_STATE;
3246            $self->{s_kwd} = 0;
3247            ## Reconsume.
3248            redo A;
3249          } else {
3250            !!!parse-error (type => 'bare hcro',
3251                            line => $self->{line_prev},
3252                            column => $self->{column_prev} - 2);
3253    
3254            ## NOTE: According to the spec algorithm, nothing is returned,
3255            ## and then "&#" followed by "X" or "x" is appended to the parent
3256            ## element or the attribute value in the later processing.
3257    
3258            if ($self->{prev_state} == DATA_STATE) {
3259              !!!cp (1005);
3260              $self->{state} = $self->{prev_state};
3261              ## Reconsume.
3262              !!!emit ({type => CHARACTER_TOKEN,
3263                        data => '&' . $self->{s_kwd},
3264                        line => $self->{line_prev},
3265                        column => $self->{column_prev} - length $self->{s_kwd},
3266                       });
3267              redo A;
3268            } else {
3269              !!!cp (989);
3270              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3271              $self->{state} = $self->{prev_state};
3272              ## Reconsume.
3273              redo A;
3274            }
3275          }
3276        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3277          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3278            # 0..9
3279            !!!cp (1002);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0030;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0061 <= $self->{nc} and
3286                   $self->{nc} <= 0x0066) { # a..f
3287            !!!cp (1003);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif (0x0041 <= $self->{nc} and
3294                   $self->{nc} <= 0x0046) { # A..F
3295            !!!cp (1004);
3296            $self->{s_kwd} *= 0x10;
3297            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3298            ## Stay in the state.
3299            !!!next-input-character;
3300            redo A;
3301          } elsif ($self->{nc} == 0x003B) { # ;
3302            !!!cp (1006);
3303            !!!next-input-character;
3304            #
3305          } else {
3306            !!!cp (1007);
3307            !!!parse-error (type => 'no refc',
3308                            line => $self->{line},
3309                            column => $self->{column});
3310            ## Reconsume.
3311            #
3312          }
3313    
3314          my $code = $self->{s_kwd};
3315          my $l = $self->{line_prev};
3316          my $c = $self->{column_prev};
3317          if ($charref_map->{$code}) {
3318            !!!cp (1008);
3319            !!!parse-error (type => 'invalid character reference',
3320                            text => (sprintf 'U+%04X', $code),
3321                            line => $l, column => $c);
3322            $code = $charref_map->{$code};
3323        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3324          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U-%08X', $code),
3327                            line => $l, column => $c);
3328          $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};  
3329        }        }
3330          
3331        return {type => CHARACTER_TOKEN, data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3332      } else {          !!!cp (988);
3333        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3334        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3335        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3336        return undef;                    line => $l, column => $c,
3337      }                   });
3338    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3339              $self->{next_input_character} <= 0x005A) or        } else {
3340             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3341              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3342      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3343      !!!next-input-character;          $self->{state} = $self->{prev_state};
3344            ## Reconsume.
3345      my $value = $entity_name;          redo A;
3346      my $match = 0;        }
3347      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3348      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3349              ## NOTE: Some number greater than the maximum length of entity name
3350      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3351             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3352             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3353               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3354              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3355               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3356              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3357               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3358              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3359        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3360        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3361          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3362            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3363            $match = 1;              $self->{entity__match} = 1;
3364            !!!next-input-character;              !!!next-input-character;
3365            last;              #
3366              } else {
3367                !!!cp (1021);
3368                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369                $self->{entity__match} = -1;
3370                ## Stay in the state.
3371                !!!next-input-character;
3372                redo A;
3373              }
3374          } else {          } else {
3375            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3376            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3377              $self->{entity__match} *= 2;
3378              ## Stay in the state.
3379            !!!next-input-character;            !!!next-input-character;
3380              redo A;
3381          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3382        }        }
3383      }  
3384              my $data;
3385      if ($match > 0) {        my $has_ref;
3386        return {type => CHARACTER_TOKEN, data => $value};        if ($self->{entity__match} > 0) {
3387      } elsif ($match < 0) {          !!!cp (1023);
3388        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3389        if ($in_attr and $match < -1) {          $has_ref = 1;
3390          return {type => CHARACTER_TOKEN, data => '&'.$entity_name};          #
3391          } elsif ($self->{entity__match} < 0) {
3392            !!!parse-error (type => 'no refc');
3393            if ($self->{prev_state} != DATA_STATE and # in attribute
3394                $self->{entity__match} < -1) {
3395              !!!cp (1024);
3396              $data = '&' . $self->{s_kwd};
3397              #
3398            } else {
3399              !!!cp (1025);
3400              $data = $self->{entity__value};
3401              $has_ref = 1;
3402              #
3403            }
3404        } else {        } else {
3405          return {type => CHARACTER_TOKEN, data => $value};          !!!cp (1026);
3406            !!!parse-error (type => 'bare ero',
3407                            line => $self->{line_prev},
3408                            column => $self->{column_prev} - length $self->{s_kwd});
3409            $data = '&' . $self->{s_kwd};
3410            #
3411          }
3412      
3413          ## NOTE: In these cases, when a character reference is found,
3414          ## it is consumed and a character token is returned, or, otherwise,
3415          ## nothing is consumed and returned, according to the spec algorithm.
3416          ## In this implementation, anything that has been examined by the
3417          ## tokenizer is appended to the parent element or the attribute value
3418          ## as string, either literal string when no character reference or
3419          ## entity-replaced string otherwise, in this stage, since any characters
3420          ## that would not be consumed are appended in the data state or in an
3421          ## appropriate attribute value state anyway.
3422    
3423          if ($self->{prev_state} == DATA_STATE) {
3424            !!!cp (986);
3425            $self->{state} = $self->{prev_state};
3426            ## Reconsume.
3427            !!!emit ({type => CHARACTER_TOKEN,
3428                      data => $data,
3429                      line => $self->{line_prev},
3430                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3431                     });
3432            redo A;
3433          } else {
3434            !!!cp (985);
3435            $self->{ca}->{value} .= $data;
3436            $self->{ca}->{has_reference} = 1 if $has_ref;
3437            $self->{state} = $self->{prev_state};
3438            ## Reconsume.
3439            redo A;
3440        }        }
3441      } else {      } else {
3442        !!!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};  
3443      }      }
3444    } else {    } # A  
3445      ## no characters are consumed  
3446      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3447      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3448    
3449  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3450    my $self = shift;    my $self = shift;
# Line 1815  sub _initialize_tree_constructor ($) { Line 3453  sub _initialize_tree_constructor ($) {
3453    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3454    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3455    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3456      $self->{document}->set_user_data (manakai_source_line => 1);
3457      $self->{document}->set_user_data (manakai_source_column => 1);
3458  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3459    
3460  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1834  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3475    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3476    ## 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  
3477        
3478    !!!next-token;    !!!next-token;
3479    
   $self->{insertion_mode} = BEFORE_HEAD_IM;  
3480    undef $self->{form_element};    undef $self->{form_element};
3481    undef $self->{head_element};    undef $self->{head_element};
3482      undef $self->{head_element_inserted};
3483    $self->{open_elements} = [];    $self->{open_elements} = [];
3484    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3485      undef $self->{ignore_newline};
3486    
3487      ## NOTE: The "initial" insertion mode.
3488    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3489    
3490      ## NOTE: The "before html" insertion mode.
3491    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3492      $self->{insertion_mode} = BEFORE_HEAD_IM;
3493    
3494      ## NOTE: The "before head" insertion mode and so on.
3495    $self->_tree_construction_main;    $self->_tree_construction_main;
3496  } # _construct_tree  } # _construct_tree
3497    
3498  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3499    my $self = shift;    my $self = shift;
3500    
3501      ## NOTE: "initial" insertion mode
3502    
3503    INITIAL: {    INITIAL: {
3504      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
3505        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
# Line 1861  sub _tree_construction_initial ($) { Line 3507  sub _tree_construction_initial ($) {
3507        ## language.        ## language.
3508        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3509        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3510        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3511        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3512            defined $token->{public_identifier} or            defined $token->{sysid}) {
3513            defined $token->{system_identifier}) {          !!!cp ('t1');
3514          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3515        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3516          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3517          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3518          } elsif (defined $token->{pubid}) {
3519            if ($token->{pubid} eq 'XSLT-compat') {
3520              !!!cp ('t1.2');
3521              !!!parse-error (type => 'XSLT-compat', token => $token,
3522                              level => $self->{level}->{should});
3523            } else {
3524              !!!parse-error (type => 'not HTML5', token => $token);
3525            }
3526          } else {
3527            !!!cp ('t3');
3528            #
3529        }        }
3530                
3531        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3532          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3533        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3534            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3535        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3536            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3537        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3538        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3539        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3540                
3541        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3542            !!!cp ('t4');
3543          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3544        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3545          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3546          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3547          if ({          my $prefix = [
3548            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3549            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3550            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3551            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3552            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3553            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3554            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3555            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3556            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3557            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3558            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3559            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3560            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3561            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3562            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3563            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3564            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3565            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3566            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3567            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3568            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3569            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3570            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3571            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3572            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3573            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3574            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3575            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3576            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3577            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3578            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3579            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3580            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3581            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3582            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3583            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3584            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3585            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3587            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3588            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3589            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3590            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3591            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3592            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3593            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3594            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3595            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3596            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3597            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3598            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3599            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3600            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3601            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3602            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3603            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3604            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3605            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3606            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3607            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3608            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3609            "-//W3C//DTD W3 HTML//EN" => 1,            }
3610            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3611            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3612            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3613            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3614            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3615            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3616            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3617          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3618                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3619            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3620                !!!cp ('t6');
3621              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3622            } else {            } else {
3623                !!!cp ('t7');
3624              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3625            }            }
3626          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3627                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3628              !!!cp ('t8');
3629            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3630            } else {
3631              !!!cp ('t9');
3632          }          }
3633          } else {
3634            !!!cp ('t10');
3635        }        }
3636        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3637          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3638          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3639          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") {
3640              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3641              ## marked as quirks.
3642            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3643              !!!cp ('t11');
3644            } else {
3645              !!!cp ('t12');
3646          }          }
3647          } else {
3648            !!!cp ('t13');
3649        }        }
3650                
3651        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3652        !!!next-token;        !!!next-token;
3653        return;        return;
3654      } elsif ({      } elsif ({
# Line 1986  sub _tree_construction_initial ($) { Line 3656  sub _tree_construction_initial ($) {
3656                END_TAG_TOKEN, 1,                END_TAG_TOKEN, 1,
3657                END_OF_FILE_TOKEN, 1,                END_OF_FILE_TOKEN, 1,
3658               }->{$token->{type}}) {               }->{$token->{type}}) {
3659        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3660          !!!parse-error (type => 'no DOCTYPE', token => $token);
3661        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3662        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3663        ## reprocess        ## reprocess
3664          !!!ack-later;
3665        return;        return;
3666      } elsif ($token->{type} == CHARACTER_TOKEN) {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3667        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3668          ## Ignore the token          ## Ignore the token
3669    
3670          unless (length $token->{data}) {          unless (length $token->{data}) {
3671            ## Stay in the phase            !!!cp ('t15');
3672              ## Stay in the insertion mode.
3673            !!!next-token;            !!!next-token;
3674            redo INITIAL;            redo INITIAL;
3675            } else {
3676              !!!cp ('t16');
3677          }          }
3678          } else {
3679            !!!cp ('t17');
3680        }        }
3681    
3682        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3683        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3684        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3685        ## reprocess        ## reprocess
3686        return;        return;
3687      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
3688          !!!cp ('t18');
3689        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3690        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3691                
3692        ## Stay in the phase.        ## Stay in the insertion mode.
3693        !!!next-token;        !!!next-token;
3694        redo INITIAL;        redo INITIAL;
3695      } else {      } else {
3696        die "$0: $token->{type}: Unknown token type";        die "$0: $token->{type}: Unknown token type";
3697      }      }
3698    } # INITIAL    } # INITIAL
3699    
3700      die "$0: _tree_construction_initial: This should be never reached";
3701  } # _tree_construction_initial  } # _tree_construction_initial
3702    
3703  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3704    my $self = shift;    my $self = shift;
3705    
3706      ## NOTE: "before html" insertion mode.
3707        
3708    B: {    B: {
3709        if ($token->{type} == DOCTYPE_TOKEN) {        if ($token->{type} == DOCTYPE_TOKEN) {
3710          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3711            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3712          ## Ignore the token          ## Ignore the token
3713          ## Stay in the phase          ## Stay in the insertion mode.
3714          !!!next-token;          !!!next-token;
3715          redo B;          redo B;
3716        } elsif ($token->{type} == COMMENT_TOKEN) {        } elsif ($token->{type} == COMMENT_TOKEN) {
3717            !!!cp ('t20');
3718          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3719          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3720          ## Stay in the phase          ## Stay in the insertion mode.
3721          !!!next-token;          !!!next-token;
3722          redo B;          redo B;
3723        } elsif ($token->{type} == CHARACTER_TOKEN) {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3724          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3725            ## Ignore the token.            ## Ignore the token.
3726    
3727            unless (length $token->{data}) {            unless (length $token->{data}) {
3728              ## Stay in the phase              !!!cp ('t21');
3729                ## Stay in the insertion mode.
3730              !!!next-token;              !!!next-token;
3731              redo B;              redo B;
3732              } else {
3733                !!!cp ('t22');
3734            }            }
3735            } else {
3736              !!!cp ('t23');
3737          }          }
3738    
3739            $self->{application_cache_selection}->(undef);
3740    
3741          #          #
3742          } elsif ($token->{type} == START_TAG_TOKEN) {
3743            if ($token->{tag_name} eq 'html') {
3744              my $root_element;
3745              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3746              $self->{document}->append_child ($root_element);
3747              push @{$self->{open_elements}},
3748                  [$root_element, $el_category->{html}];
3749    
3750              if ($token->{attributes}->{manifest}) {
3751                !!!cp ('t24');
3752                $self->{application_cache_selection}
3753                    ->($token->{attributes}->{manifest}->{value});
3754                ## ISSUE: Spec is unclear on relative references.
3755                ## According to Hixie (#whatwg 2008-03-19), it should be
3756                ## resolved against the base URI of the document in HTML
3757                ## or xml:base of the element in XHTML.
3758              } else {
3759                !!!cp ('t25');
3760                $self->{application_cache_selection}->(undef);
3761              }
3762    
3763              !!!nack ('t25c');
3764    
3765              !!!next-token;
3766              return; ## Go to the "before head" insertion mode.
3767            } else {
3768              !!!cp ('t25.1');
3769              #
3770            }
3771        } elsif ({        } elsif ({
                 START_TAG_TOKEN, 1,  
3772                  END_TAG_TOKEN, 1,                  END_TAG_TOKEN, 1,
3773                  END_OF_FILE_TOKEN, 1,                  END_OF_FILE_TOKEN, 1,
3774                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3775          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3776          #          #
3777        } else {        } else {
3778          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3779        }        }
3780        my $root_element; !!!create-element ($root_element, 'html');  
3781        $self->{document}->append_child ($root_element);      my $root_element;
3782        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3783        ## reprocess      $self->{document}->append_child ($root_element);
3784        #redo B;      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3785        return; ## Go to the main phase.  
3786        $self->{application_cache_selection}->(undef);
3787    
3788        ## NOTE: Reprocess the token.
3789        !!!ack-later;
3790        return; ## Go to the "before head" insertion mode.
3791    } # B    } # B
3792    
3793      die "$0: _tree_construction_root_element: This should never be reached";
3794  } # _tree_construction_root_element  } # _tree_construction_root_element
3795    
3796  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2078  sub _reset_insertion_mode ($) { Line 3805  sub _reset_insertion_mode ($) {
3805            
3806      ## Step 3      ## Step 3
3807      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"!?  
3808        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3809          $last = 1;          $last = 1;
3810          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3811            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3812                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3813              #          } else {
3814            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3815          }          }
3816        }        }
3817              
3818        ## Step 4..13        ## Step 4..14
3819        my $new_mode = {        my $new_mode;
3820          if ($node->[1] & FOREIGN_EL) {
3821            !!!cp ('t28.1');
3822            ## NOTE: Strictly spaking, the line below only applies to MathML and
3823            ## SVG elements.  Currently the HTML syntax supports only MathML and
3824            ## SVG elements as foreigners.
3825            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3826          } elsif ($node->[1] == TABLE_CELL_EL) {
3827            if ($last) {
3828              !!!cp ('t28.2');
3829              #
3830            } else {
3831              !!!cp ('t28.3');
3832              $new_mode = IN_CELL_IM;
3833            }
3834          } else {
3835            !!!cp ('t28.4');
3836            $new_mode = {
3837                        select => IN_SELECT_IM,                        select => IN_SELECT_IM,
3838                        td => IN_CELL_IM,                        ## NOTE: |option| and |optgroup| do not set
3839                        th => IN_CELL_IM,                        ## insertion mode to "in select" by themselves.
3840                        tr => IN_ROW_IM,                        tr => IN_ROW_IM,
3841                        tbody => IN_TABLE_BODY_IM,                        tbody => IN_TABLE_BODY_IM,
3842                        thead => IN_TABLE_BODY_IM,                        thead => IN_TABLE_BODY_IM,
# Line 2110  sub _reset_insertion_mode ($) { Line 3847  sub _reset_insertion_mode ($) {
3847                        head => IN_BODY_IM, # not in head!                        head => IN_BODY_IM, # not in head!
3848                        body => IN_BODY_IM,                        body => IN_BODY_IM,
3849                        frameset => IN_FRAMESET_IM,                        frameset => IN_FRAMESET_IM,
3850                       }->{$node->[1]};                       }->{$node->[0]->manakai_local_name};
3851          }
3852        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3853                
3854        ## Step 14        ## Step 15
3855        if ($node->[1] eq 'html') {        if ($node->[1] == HTML_EL) {
3856          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3857              !!!cp ('t29');
3858            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
3859          } else {          } else {
3860              ## ISSUE: Can this state be reached?
3861              !!!cp ('t30');
3862            $self->{insertion_mode} = AFTER_HEAD_IM;            $self->{insertion_mode} = AFTER_HEAD_IM;
3863          }          }
3864          return;          return;
3865          } else {
3866            !!!cp ('t31');
3867        }        }
3868                
3869        ## Step 15        ## Step 16
3870        $self->{insertion_mode} = IN_BODY_IM and return if $last;        $self->{insertion_mode} = IN_BODY_IM and return if $last;
3871                
3872        ## Step 16        ## Step 17
3873        $i--;        $i--;
3874        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3875                
3876        ## Step 17        ## Step 18
3877        redo S3;        redo S3;
3878      } # S3      } # S3
3879    
3880      die "$0: _reset_insertion_mode: This line should never be reached";
3881  } # _reset_insertion_mode  } # _reset_insertion_mode
3882    
3883  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
# Line 2154  sub _tree_construction_main ($) { Line 3899  sub _tree_construction_main ($) {
3899      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3900      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3901        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3902            !!!cp ('t32');
3903          return;          return;
3904        }        }
3905      }      }
# Line 2168  sub _tree_construction_main ($) { Line 3914  sub _tree_construction_main ($) {
3914    
3915        ## Step 6        ## Step 6
3916        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3917            !!!cp ('t33_1');
3918          #          #
3919        } else {        } else {
3920          my $in_open_elements;          my $in_open_elements;
3921          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3922            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3923                !!!cp ('t33');
3924              $in_open_elements = 1;              $in_open_elements = 1;
3925              last OE;              last OE;
3926            }            }
3927          }          }
3928          if ($in_open_elements) {          if ($in_open_elements) {
3929              !!!cp ('t34');
3930            #            #
3931          } else {          } else {
3932              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3933              !!!cp ('t35');
3934            redo S4;            redo S4;
3935          }          }
3936        }        }
# Line 2202  sub _tree_construction_main ($) { Line 3953  sub _tree_construction_main ($) {
3953    
3954        ## Step 11        ## Step 11
3955        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3956            !!!cp ('t36');
3957          ## Step 7'          ## Step 7'
3958          $i++;          $i++;
3959          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3960                    
3961          redo S7;          redo S7;
3962        }        }
3963    
3964          !!!cp ('t37');
3965      } # S7      } # S7
3966    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3967    
3968    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3969      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3970        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3971            !!!cp ('t38');
3972          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3973          return;          return;
3974        }        }
3975      }      }
3976    
3977        !!!cp ('t39');
3978    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3979    
3980    my $parse_rcdata = sub ($$) {    my $insert;
3981      my ($content_model_flag, $insert) = @_;  
3982      my $parse_rcdata = sub ($) {
3983        my ($content_model_flag) = @_;
3984    
3985      ## Step 1      ## Step 1
3986      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3987      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3988    
3989      ## Step 2      ## Step 2
     $insert->($el); # /context node/->append_child ($el)  
   
     ## Step 3  
3990      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3991      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3992    
3993      ## Step 4      ## Step 3, 4
3994      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
3995    
3996      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and $token->{tag_name} eq $start_tag_name) {  
       ## Ignore the token  
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
     } else {  
       die "$0: $content_model_flag in parse_rcdata";  
     }  
3997      !!!next-token;      !!!next-token;
3998    }; # $parse_rcdata    }; # $parse_rcdata
3999    
4000    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
4001      my $insert = $_[0];      ## Step 1
4002      my $script_el;      my $script_el;
4003      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4004    
4005        ## Step 2
4006      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4007    
4008        ## Step 3
4009        ## TODO: Mark as "already executed", if ...
4010    
4011        ## Step 4
4012        $insert->($script_el);
4013    
4014        ## ISSUE: $script_el is not put into the stack
4015        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4016    
4017        ## Step 5
4018      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4019      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;  
4020    
4021      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
4022          $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  
4023    
4024        $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...  
     }  
       
4025      !!!next-token;      !!!next-token;
4026    }; # $script_start_tag    }; # $script_start_tag
4027    
4028      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4029      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4030      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4031      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4032    
4033    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4034      my $tag_name = shift;      my $end_tag_token = shift;
4035        my $tag_name = $end_tag_token->{tag_name};
4036    
4037        ## NOTE: The adoption agency algorithm (AAA).
4038    
4039      FET: {      FET: {
4040        ## Step 1        ## Step 1
4041        my $formatting_element;        my $formatting_element;
4042        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4043        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4044          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4045              !!!cp ('t52');
4046              last AFE;
4047            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4048                         eq $tag_name) {
4049              !!!cp ('t51');
4050            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4051            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4052            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4053          }          }
4054        } # AFE        } # AFE
4055        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4056          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4057            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4058          ## Ignore the token          ## Ignore the token
4059          !!!next-token;          !!!next-token;
4060          return;          return;
# Line 2340  sub _tree_construction_main ($) { Line 4066  sub _tree_construction_main ($) {
4066          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4067          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4068            if ($in_scope) {            if ($in_scope) {
4069                !!!cp ('t54');
4070              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4071              last INSCOPE;              last INSCOPE;
4072            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4073              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4074                !!!parse-error (type => 'unmatched end tag',
4075                                text => $token->{tag_name},
4076                                token => $end_tag_token);
4077              ## Ignore the token              ## Ignore the token
4078              !!!next-token;              !!!next-token;
4079              return;              return;
4080            }            }
4081          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4082                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4083            $in_scope = 0;            $in_scope = 0;
4084          }          }
4085        } # INSCOPE        } # INSCOPE
4086        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4087          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4088            !!!parse-error (type => 'unmatched end tag',
4089                            text => $token->{tag_name},
4090                            token => $end_tag_token);
4091          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4092          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4093          return;          return;
4094        }        }
4095        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4096          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4097            !!!parse-error (type => 'not closed',
4098                            text => $self->{open_elements}->[-1]->[0]
4099                                ->manakai_local_name,
4100                            token => $end_tag_token);
4101        }        }
4102                
4103        ## Step 2        ## Step 2
# Line 2370  sub _tree_construction_main ($) { Line 4105  sub _tree_construction_main ($) {
4105        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4106        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4107          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4108          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4109              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4110              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4111               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4112              !!!cp ('t59');
4113            $furthest_block = $node;            $furthest_block = $node;
4114            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4115              ## NOTE: The topmost (eldest) node.
4116          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4117              !!!cp ('t60');
4118            last OE;            last OE;
4119          }          }
4120        } # OE        } # OE
4121                
4122        ## Step 3        ## Step 3
4123        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4124            !!!cp ('t61');
4125          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4126          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4127          !!!next-token;          !!!next-token;
# Line 2395  sub _tree_construction_main ($) { Line 4134  sub _tree_construction_main ($) {
4134        ## Step 5        ## Step 5
4135        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4136        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4137            !!!cp ('t62');
4138          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4139        }        }
4140                
# Line 2417  sub _tree_construction_main ($) { Line 4157  sub _tree_construction_main ($) {
4157          S7S2: {          S7S2: {
4158            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4159              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4160                  !!!cp ('t63');
4161                $node_i_in_active = $_;                $node_i_in_active = $_;
4162                last S7S2;                last S7S2;
4163              }              }
# Line 2430  sub _tree_construction_main ($) { Line 4171  sub _tree_construction_main ($) {
4171                    
4172          ## Step 4          ## Step 4
4173          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4174              !!!cp ('t64');
4175            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4176          }          }
4177                    
4178          ## Step 5          ## Step 5
4179          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4180              !!!cp ('t65');
4181            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4182            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4183            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2452  sub _tree_construction_main ($) { Line 4195  sub _tree_construction_main ($) {
4195        } # S7          } # S7  
4196                
4197        ## Step 8        ## Step 8
4198        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4199            my $foster_parent_element;
4200            my $next_sibling;
4201            OE: for (reverse 0..$#{$self->{open_elements}}) {
4202              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4203                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4204                                 if (defined $parent and $parent->node_type == 1) {
4205                                   !!!cp ('t65.1');
4206                                   $foster_parent_element = $parent;
4207                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4208                                 } else {
4209                                   !!!cp ('t65.2');
4210                                   $foster_parent_element
4211                                     = $self->{open_elements}->[$_ - 1]->[0];
4212                                 }
4213                                 last OE;
4214                               }
4215                             } # OE
4216                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4217                               unless defined $foster_parent_element;
4218            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4219            $open_tables->[-1]->[1] = 1; # tainted
4220          } else {
4221            !!!cp ('t65.3');
4222            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4223          }
4224                
4225        ## Step 9        ## Step 9
4226        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2469  sub _tree_construction_main ($) { Line 4237  sub _tree_construction_main ($) {
4237        my $i;        my $i;
4238        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4239          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4240              !!!cp ('t66');
4241            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4242            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4243          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4244              !!!cp ('t67');
4245            $i = $_;            $i = $_;
4246          }          }
4247        } # AFE        } # AFE
# Line 2481  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        undef $i;        undef $i;
4252        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4253          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4254              !!!cp ('t68');
4255            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4256            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4257          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4258              !!!cp ('t69');
4259            $i = $_;            $i = $_;
4260          }          }
4261        } # OE        } # OE
4262        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4263                
4264        ## Step 14        ## Step 14
4265        redo FET;        redo FET;
4266      } # FET      } # FET
4267    }; # $formatting_end_tag    }; # $formatting_end_tag
4268    
4269    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4270      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4271    }; # $insert_to_current    }; # $insert_to_current
4272    
4273    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4274                         my $child = shift;      my $child = shift;
4275                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4276                              table => 1, tbody => 1, tfoot => 1,        # MUST
4277                              thead => 1, tr => 1,        my $foster_parent_element;
4278                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4279                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4280                           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') {  
4281                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4282                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4283                                   !!!cp ('t70');
4284                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4285                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4286                               } else {                               } else {
4287                                   !!!cp ('t71');
4288                                 $foster_parent_element                                 $foster_parent_element
4289                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4290                               }                               }
# Line 2524  sub _tree_construction_main ($) { Line 4295  sub _tree_construction_main ($) {
4295                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4296                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4297                             ($child, $next_sibling);                             ($child, $next_sibling);
4298                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4299                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4300                         }        !!!cp ('t72');
4301          $self->{open_elements}->[-1]->[0]->append_child ($child);
4302        }
4303    }; # $insert_to_foster    }; # $insert_to_foster
4304    
4305    my $insert;    ## NOTE: Insert a character (MUST): When a character is inserted, if
4306      ## the last node that was inserted by the parser is a Text node and
4307      ## the character has to be inserted after that node, then the
4308      ## character is appended to the Text node.  However, if any other
4309      ## node is inserted by the parser, then a new Text node is created
4310      ## and the character is appended as that Text node.  If I'm not
4311      ## wrong, for a parser with scripting disabled, there are only two
4312      ## cases where this occurs.  One is the case where an element node
4313      ## is inserted to the |head| element.  This is covered by using the
4314      ## |$self->{head_element_inserted}| flag.  Another is the case where
4315      ## an element or comment is inserted into the |table| subtree while
4316      ## foster parenting happens.  This is covered by using the [2] flag
4317      ## of the |$open_tables| structure.  All other cases are handled
4318      ## simply by calling |manakai_append_text| method.
4319    
4320      ## TODO: |<body><script>document.write("a<br>");
4321      ## document.body.removeChild (document.body.lastChild);
4322      ## document.write ("b")</script>|
4323    
4324    B: {    B: while (1) {
4325      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
4326        !!!parse-error (type => 'DOCTYPE in the middle');        !!!cp ('t73');
4327          !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4328        ## Ignore the token        ## Ignore the token
4329        ## Stay in the phase        ## Stay in the phase
4330        !!!next-token;        !!!next-token;
4331        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;  
4332      } elsif ($token->{type} == START_TAG_TOKEN and      } elsif ($token->{type} == START_TAG_TOKEN and
4333               $token->{tag_name} eq 'html') {               $token->{tag_name} eq 'html') {
4334        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4335          ## Turn into the main phase          !!!cp ('t79');
4336          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4337          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
4338        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4339          ## Turn into the main phase          !!!cp ('t80');
4340          !!!parse-error (type => 'after html:html');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4341          $self->{insertion_mode} = AFTER_FRAMESET_IM;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4342          } else {
4343            !!!cp ('t81');
4344        }        }
4345    
4346  ## ISSUE: "aa<html>" is not a parse error.        !!!cp ('t82');
4347  ## 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');  
       }  
4348        my $top_el = $self->{open_elements}->[0]->[0];        my $top_el = $self->{open_elements}->[0]->[0];
4349        for my $attr_name (keys %{$token->{attributes}}) {        for my $attr_name (keys %{$token->{attributes}}) {
4350          unless ($top_el->has_attribute_ns (undef, $attr_name)) {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4351              !!!cp ('t84');
4352            $top_el->set_attribute_ns            $top_el->set_attribute_ns
4353              (undef, [undef, $attr_name],              (undef, [undef, $attr_name],
4354               $token->{attributes}->{$attr_name}->{value});               $token->{attributes}->{$attr_name}->{value});
4355          }          }
4356        }        }
4357          !!!nack ('t84.1');
4358        !!!next-token;        !!!next-token;
4359        redo B;        next B;
4360      } elsif ($token->{type} == COMMENT_TOKEN) {      } elsif ($token->{type} == COMMENT_TOKEN) {
4361        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
4362        if ($self->{insertion_mode} & AFTER_HTML_IMS) {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4363            !!!cp ('t85');
4364          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
4365        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4366            !!!cp ('t86');
4367          $self->{open_elements}->[0]->[0]->append_child ($comment);          $self->{open_elements}->[0]->[0]->append_child ($comment);
4368        } else {        } else {
4369            !!!cp ('t87');
4370          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4371            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4372        }        }
4373        !!!next-token;        !!!next-token;
4374        redo B;        next B;
4375      } elsif ($self->{insertion_mode} & HEAD_IMS) {      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4376        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
4377          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4378            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          delete $self->{ignore_newline};
4379    
4380            if (length $token->{data}) {
4381              !!!cp ('t43');
4382              $self->{open_elements}->[-1]->[0]->manakai_append_text
4383                  ($token->{data});
4384            } else {
4385              !!!cp ('t43.1');
4386            }
4387            !!!next-token;
4388            next B;
4389          } elsif ($token->{type} == END_TAG_TOKEN) {
4390            delete $self->{ignore_newline};
4391    
4392            if ($token->{tag_name} eq 'script') {
4393              !!!cp ('t50');
4394              
4395              ## Para 1-2
4396              my $script = pop @{$self->{open_elements}};
4397              
4398              ## Para 3
4399              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4400    
4401              ## Para 4
4402              ## TODO: $old_insertion_point = $current_insertion_point;
4403              ## TODO: $current_insertion_point = just before $self->{nc};
4404    
4405              ## Para 5
4406              ## TODO: Run the $script->[0].
4407    
4408              ## Para 6
4409              ## TODO: $current_insertion_point = $old_insertion_point;
4410    
4411              ## Para 7
4412              ## TODO: if ($pending_external_script) {
4413                ## TODO: ...
4414              ## TODO: }
4415    
4416              !!!next-token;
4417              next B;
4418            } else {
4419              !!!cp ('t42');
4420    
4421              pop @{$self->{open_elements}};
4422    
4423              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4424              !!!next-token;
4425              next B;
4426            }
4427          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4428            delete $self->{ignore_newline};
4429    
4430            !!!cp ('t44');
4431            !!!parse-error (type => 'not closed',
4432                            text => $self->{open_elements}->[-1]->[0]
4433                                ->manakai_local_name,
4434                            token => $token);
4435    
4436            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4437            #  ## TODO: Mark as "already executed"
4438            #}
4439    
4440            pop @{$self->{open_elements}};
4441    
4442            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4443            ## Reprocess.
4444            next B;
4445          } else {
4446            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4447          }
4448        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4449          if ($token->{type} == CHARACTER_TOKEN) {
4450            !!!cp ('t87.1');
4451            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4452            !!!next-token;
4453            next B;
4454          } elsif ($token->{type} == START_TAG_TOKEN) {
4455            if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4456                 $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4457                not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4458                ($token->{tag_name} eq 'svg' and
4459                 $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4460              ## NOTE: "using the rules for secondary insertion mode"then"continue"
4461              !!!cp ('t87.2');
4462              #
4463            } elsif ({
4464                      b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4465                      center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4466                      em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4467                      h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4468                      img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4469                      nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4470                      small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4471                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4472                     }->{$token->{tag_name}}) {
4473              !!!cp ('t87.2');
4474              !!!parse-error (type => 'not closed',
4475                              text => $self->{open_elements}->[-1]->[0]
4476                                  ->manakai_local_name,
4477                              token => $token);
4478    
4479              pop @{$self->{open_elements}}
4480                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4481    
4482              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4483              ## Reprocess.
4484              next B;
4485            } else {
4486              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4487              my $tag_name = $token->{tag_name};
4488              if ($nsuri eq $SVG_NS) {
4489                $tag_name = {
4490                   altglyph => 'altGlyph',
4491                   altglyphdef => 'altGlyphDef',
4492                   altglyphitem => 'altGlyphItem',
4493                   animatecolor => 'animateColor',
4494                   animatemotion => 'animateMotion',
4495                   animatetransform => 'animateTransform',
4496                   clippath => 'clipPath',
4497                   feblend => 'feBlend',
4498                   fecolormatrix => 'feColorMatrix',
4499                   fecomponenttransfer => 'feComponentTransfer',
4500                   fecomposite => 'feComposite',
4501                   feconvolvematrix => 'feConvolveMatrix',
4502                   fediffuselighting => 'feDiffuseLighting',
4503                   fedisplacementmap => 'feDisplacementMap',
4504                   fedistantlight => 'feDistantLight',
4505                   feflood => 'feFlood',
4506                   fefunca => 'feFuncA',
4507                   fefuncb => 'feFuncB',
4508                   fefuncg => 'feFuncG',
4509                   fefuncr => 'feFuncR',
4510                   fegaussianblur => 'feGaussianBlur',
4511                   feimage => 'feImage',
4512                   femerge => 'feMerge',
4513                   femergenode => 'feMergeNode',
4514                   femorphology => 'feMorphology',
4515                   feoffset => 'feOffset',
4516                   fepointlight => 'fePointLight',
4517                   fespecularlighting => 'feSpecularLighting',
4518                   fespotlight => 'feSpotLight',
4519                   fetile => 'feTile',
4520                   feturbulence => 'feTurbulence',
4521                   foreignobject => 'foreignObject',
4522                   glyphref => 'glyphRef',
4523                   lineargradient => 'linearGradient',
4524                   radialgradient => 'radialGradient',
4525                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4526                   textpath => 'textPath',  
4527                }->{$tag_name} || $tag_name;
4528              }
4529    
4530              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4531    
4532              ## "adjust foreign attributes" - done in insert-element-f
4533    
4534              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4535    
4536              if ($self->{self_closing}) {
4537                pop @{$self->{open_elements}};
4538                !!!ack ('t87.3');
4539              } else {
4540                !!!cp ('t87.4');
4541              }
4542    
4543              !!!next-token;
4544              next B;
4545            }
4546          } elsif ($token->{type} == END_TAG_TOKEN) {
4547            ## NOTE: "using the rules for secondary insertion mode" then "continue"
4548            !!!cp ('t87.5');
4549            #
4550          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4551            !!!cp ('t87.6');
4552            !!!parse-error (type => 'not closed',
4553                            text => $self->{open_elements}->[-1]->[0]
4554                                ->manakai_local_name,
4555                            token => $token);
4556    
4557            pop @{$self->{open_elements}}
4558                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4559    
4560            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4561    
4562            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4563            ## Reprocess.
4564            next B;
4565          } else {
4566            die "$0: $token->{type}: Unknown token type";        
4567          }
4568        }
4569    
4570        if ($self->{insertion_mode} & HEAD_IMS) {
4571          if ($token->{type} == CHARACTER_TOKEN) {
4572            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4573              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4574                if ($self->{head_element_inserted}) {
4575                  !!!cp ('t88.3');
4576                  $self->{open_elements}->[-1]->[0]->append_child
4577                    ($self->{document}->create_text_node ($1));
4578                  delete $self->{head_element_inserted};
4579                  ## NOTE: |</head> <link> |
4580                  #
4581                } else {
4582                  !!!cp ('t88.2');
4583                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4584                  ## NOTE: |</head> &#x20;|
4585                  #
4586                }
4587              } else {
4588                !!!cp ('t88.1');
4589                ## Ignore the token.
4590                #
4591              }
4592            unless (length $token->{data}) {            unless (length $token->{data}) {
4593                !!!cp ('t88');
4594              !!!next-token;              !!!next-token;
4595              redo B;              next B;
4596            }            }
4597    ## TODO: set $token->{column} appropriately
4598          }          }
4599    
4600          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4601              !!!cp ('t89');
4602            ## As if <head>            ## As if <head>
4603            !!!create-element ($self->{head_element}, 'head');            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4604            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4605            push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            push @{$self->{open_elements}},
4606                  [$self->{head_element}, $el_category->{head}];
4607    
4608            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4609            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4610    
4611            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4612          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4613              !!!cp ('t90');
4614            ## As if </noscript>            ## As if </noscript>
4615            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4616            !!!parse-error (type => 'in noscript:#character');            !!!parse-error (type => 'in noscript:#text', token => $token);
4617                        
4618            ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4619            ## As if </head>            ## As if </head>
# Line 2635  sub _tree_construction_main ($) { Line 4621  sub _tree_construction_main ($) {
4621    
4622            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4623          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4624              !!!cp ('t91');
4625            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4626    
4627            ## Reprocess in the "after head" insertion mode...            ## Reprocess in the "after head" insertion mode...
4628            } else {
4629              !!!cp ('t92');
4630          }          }
4631    
4632              ## "after head" insertion mode          ## "after head" insertion mode
4633              ## As if <body>          ## As if <body>
4634              !!!insert-element ('body');          !!!insert-element ('body',, $token);
4635              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
4636              ## reprocess          ## reprocess
4637              redo B;          next B;
4638            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4639              if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
4640                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4641                  !!!create-element ($self->{head_element}, $token->{tag_name}, $token->{attributes});              !!!cp ('t93');
4642                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4643                  push @{$self->{open_elements}}, [$self->{head_element}, $token->{tag_name}];              $self->{open_elements}->[-1]->[0]->append_child
4644                  $self->{insertion_mode} = IN_HEAD_IM;                  ($self->{head_element});
4645                  !!!next-token;              push @{$self->{open_elements}},
4646                  redo B;                  [$self->{head_element}, $el_category->{head}];
4647                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4648                  #              !!!nack ('t93.1');
4649                } else {              !!!next-token;
4650                  !!!parse-error (type => 'in head:head'); # or in head noscript              next B;
4651                  ## Ignore the token            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4652                  !!!next-token;              !!!cp ('t93.2');
4653                  redo B;              !!!parse-error (type => 'after head', text => 'head',
4654                }                              token => $token);
4655              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              ## Ignore the token
4656                ## As if <head>              !!!nack ('t93.3');
4657                !!!create-element ($self->{head_element}, 'head');              !!!next-token;
4658                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              next B;
4659                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4660                !!!cp ('t95');
4661                !!!parse-error (type => 'in head:head',
4662                                token => $token); # or in head noscript
4663                ## Ignore the token
4664                !!!nack ('t95.1');
4665                !!!next-token;
4666                next B;
4667              }
4668            } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4669              !!!cp ('t96');
4670              ## As if <head>
4671              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4672              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4673              push @{$self->{open_elements}},
4674                  [$self->{head_element}, $el_category->{head}];
4675    
4676                $self->{insertion_mode} = IN_HEAD_IM;            $self->{insertion_mode} = IN_HEAD_IM;
4677                ## Reprocess in the "in head" insertion mode...            ## Reprocess in the "in head" insertion mode...
4678              }          } else {
4679              !!!cp ('t97');
4680            }
4681    
4682              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
4683                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4684                  ## As if </noscript>              !!!cp ('t98');
4685                  pop @{$self->{open_elements}};              ## As if </noscript>
4686                  !!!parse-error (type => 'in noscript:base');              pop @{$self->{open_elements}};
4687                              !!!parse-error (type => 'in noscript', text => 'base',
4688                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4689                  ## Reprocess in the "in head" insertion mode...            
4690                }              $self->{insertion_mode} = IN_HEAD_IM;
4691                ## Reprocess in the "in head" insertion mode...
4692              } else {
4693                !!!cp ('t99');
4694              }
4695    
4696                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4697                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4698                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t100');
4699                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'after head',
4700                }                              text => $token->{tag_name}, token => $token);
4701                !!!insert-element ($token->{tag_name}, $token->{attributes});              push @{$self->{open_elements}},
4702                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  [$self->{head_element}, $el_category->{head}];
4703                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4704                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4705                !!!next-token;              !!!cp ('t101');
4706                redo B;            }
4707              } elsif ($token->{tag_name} eq 'link') {            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4708                ## NOTE: There is a "as if in head" code clone.            pop @{$self->{open_elements}};
4709                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            pop @{$self->{open_elements}} # <head>
4710                  !!!parse-error (type => 'after head:'.$token->{tag_name});                if $self->{insertion_mode} == AFTER_HEAD_IM;
4711                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!nack ('t101.1');
4712                }            !!!next-token;
4713                !!!insert-element ($token->{tag_name}, $token->{attributes});            next B;
4714                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          } elsif ($token->{tag_name} eq 'link') {
4715                pop @{$self->{open_elements}}            ## NOTE: There is a "as if in head" code clone.
4716                    if $self->{insertion_mode} == AFTER_HEAD_IM;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4717                !!!next-token;              !!!cp ('t102');
4718                redo B;              !!!parse-error (type => 'after head',
4719              } elsif ($token->{tag_name} eq 'meta') {                              text => $token->{tag_name}, token => $token);
4720                ## NOTE: There is a "as if in head" code clone.              push @{$self->{open_elements}},
4721                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  [$self->{head_element}, $el_category->{head}];
4722                  !!!parse-error (type => 'after head:'.$token->{tag_name});              $self->{head_element_inserted} = 1;
4723                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } else {
4724                }              !!!cp ('t103');
4725                !!!insert-element ($token->{tag_name}, $token->{attributes});            }
4726                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4727              pop @{$self->{open_elements}};
4728              pop @{$self->{open_elements}} # <head>
4729                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4730              !!!ack ('t103.1');
4731              !!!next-token;
4732              next B;
4733            } elsif ($token->{tag_name} eq 'command' or
4734                     $token->{tag_name} eq 'eventsource') {
4735              if ($self->{insertion_mode} == IN_HEAD_IM) {
4736                ## NOTE: If the insertion mode at the time of the emission
4737                ## of the token was "before head", $self->{insertion_mode}
4738                ## is already changed to |IN_HEAD_IM|.
4739    
4740                ## NOTE: There is a "as if in head" code clone.
4741                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4742                pop @{$self->{open_elements}};
4743                pop @{$self->{open_elements}} # <head>
4744                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4745                !!!ack ('t103.2');
4746                !!!next-token;
4747                next B;
4748              } else {
4749                ## NOTE: "in head noscript" or "after head" insertion mode
4750                ## - in these cases, these tags are treated as same as
4751                ## normal in-body tags.
4752                !!!cp ('t103.3');
4753                #
4754              }
4755            } elsif ($token->{tag_name} eq 'meta') {
4756              ## NOTE: There is a "as if in head" code clone.
4757              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4758                !!!cp ('t104');
4759                !!!parse-error (type => 'after head',
4760                                text => $token->{tag_name}, token => $token);
4761                push @{$self->{open_elements}},
4762                    [$self->{head_element}, $el_category->{head}];
4763                $self->{head_element_inserted} = 1;
4764              } else {
4765                !!!cp ('t105');
4766              }
4767              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4768              my $meta_el = pop @{$self->{open_elements}};
4769    
4770                unless ($self->{confident}) {                unless ($self->{confident}) {
4771                  my $charset;                  if ($token->{attributes}->{charset}) {
4772                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                    !!!cp ('t106');
4773                    $charset = $token->{attributes}->{charset}->{value};                    ## NOTE: Whether the encoding is supported or not is handled
4774                      ## in the {change_encoding} callback.
4775                      $self->{change_encoding}
4776                          ->($self, $token->{attributes}->{charset}->{value},
4777                             $token);
4778                      
4779                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4780                          ->set_user_data (manakai_has_reference =>
4781                                               $token->{attributes}->{charset}
4782                                                   ->{has_reference});
4783                    } elsif ($token->{attributes}->{content}) {
4784                      if ($token->{attributes}->{content}->{value}
4785                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4786                              [\x09\x0A\x0C\x0D\x20]*=
4787                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4788                              ([^"'\x09\x0A\x0C\x0D\x20]
4789                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4790                        !!!cp ('t107');
4791                        ## NOTE: Whether the encoding is supported or not is handled
4792                        ## in the {change_encoding} callback.
4793                        $self->{change_encoding}
4794                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4795                               $token);
4796                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4797                            ->set_user_data (manakai_has_reference =>
4798                                                 $token->{attributes}->{content}
4799                                                       ->{has_reference});
4800                      } else {
4801                        !!!cp ('t108');
4802                      }
4803                    }
4804                  } else {
4805                    if ($token->{attributes}->{charset}) {
4806                      !!!cp ('t109');
4807                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4808                          ->set_user_data (manakai_has_reference =>
4809                                               $token->{attributes}->{charset}
4810                                                   ->{has_reference});
4811                  }                  }
4812                  if ($token->{attributes}->{'http-equiv'}) {                  if ($token->{attributes}->{content}) {
4813                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.                    !!!cp ('t110');
4814                    if ($token->{attributes}->{'http-equiv'}->{value}                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4815                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                        ->set_user_data (manakai_has_reference =>
4816                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                                             $token->{attributes}->{content}
4817                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                                                 ->{has_reference});
                     $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
                   } ## TODO: And if supported  
4818                  }                  }
                 ## TODO: Change the encoding  
4819                }                }
4820    
4821                ## TODO: Extracting |charset| from |meta|.                pop @{$self->{open_elements}} # <head>
               pop @{$self->{open_elements}}  
4822                    if $self->{insertion_mode} == AFTER_HEAD_IM;                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4823                  !!!ack ('t110.1');
4824                !!!next-token;                !!!next-token;
4825                redo B;                next B;
4826              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
4827                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4828                  ## As if </noscript>              !!!cp ('t111');
4829                  pop @{$self->{open_elements}};              ## As if </noscript>
4830                  !!!parse-error (type => 'in noscript:title');              pop @{$self->{open_elements}};
4831                              !!!parse-error (type => 'in noscript', text => 'title',
4832                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4833                  ## Reprocess in the "in head" insertion mode...            
4834                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4835                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4836                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                }              !!!cp ('t112');
4838                !!!parse-error (type => 'after head',
4839                                text => $token->{tag_name}, token => $token);
4840                push @{$self->{open_elements}},
4841                    [$self->{head_element}, $el_category->{head}];
4842                $self->{head_element_inserted} = 1;
4843              } else {
4844                !!!cp ('t113');
4845              }
4846    
4847                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4848                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4849                    : $self->{open_elements}->[-1]->[0];            ## ISSUE: A spec bug [Bug 6038]
4850                $parse_rcdata->(RCDATA_CONTENT_MODEL,            splice @{$self->{open_elements}}, -2, 1, () # <head>
4851                                sub { $parent->append_child ($_[0]) });                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4852                pop @{$self->{open_elements}}            next B;
4853                    if $self->{insertion_mode} == AFTER_HEAD_IM;          } elsif ($token->{tag_name} eq 'style' or
4854                redo B;                   $token->{tag_name} eq 'noframes') {
4855              } elsif ($token->{tag_name} eq 'style') {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4856                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## insertion mode IN_HEAD_IM)
4857                ## insertion mode IN_HEAD_IM)            ## NOTE: There is a "as if in head" code clone.
4858                ## NOTE: There is a "as if in head" code clone.            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4859                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!cp ('t114');
4860                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4861                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                              text => $token->{tag_name}, token => $token);
4862                }              push @{$self->{open_elements}},
4863                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                  [$self->{head_element}, $el_category->{head}];
4864                pop @{$self->{open_elements}}              $self->{head_element_inserted} = 1;
4865                    if $self->{insertion_mode} == AFTER_HEAD_IM;            } else {
4866                redo B;              !!!cp ('t115');
4867              } elsif ($token->{tag_name} eq 'noscript') {            }
4868              $parse_rcdata->(CDATA_CONTENT_MODEL);
4869              ## ISSUE: A spec bug [Bug 6038]
4870              splice @{$self->{open_elements}}, -2, 1, () # <head>
4871                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4872              next B;
4873            } elsif ($token->{tag_name} eq 'noscript') {
4874                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4875                    !!!cp ('t116');
4876                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4877                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4878                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4879                    !!!nack ('t116.1');
4880                  !!!next-token;                  !!!next-token;
4881                  redo B;                  next B;
4882                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4883                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4884                    !!!parse-error (type => 'in noscript', text => 'noscript',
4885                                    token => $token);
4886                  ## Ignore the token                  ## Ignore the token
4887                    !!!nack ('t117.1');
4888                  !!!next-token;                  !!!next-token;
4889                  redo B;                  next B;
4890                } else {                } else {
4891                    !!!cp ('t118');
4892                  #                  #
4893                }                }
4894              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
4895                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4896                  ## As if </noscript>              !!!cp ('t119');
4897                  pop @{$self->{open_elements}};              ## As if </noscript>
4898                  !!!parse-error (type => 'in noscript:script');              pop @{$self->{open_elements}};
4899                              !!!parse-error (type => 'in noscript', text => 'script',
4900                  $self->{insertion_mode} = IN_HEAD_IM;                              token => $token);
4901                  ## Reprocess in the "in head" insertion mode...            
4902                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {              $self->{insertion_mode} = IN_HEAD_IM;
4903                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4904                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4905                }              !!!cp ('t120');
4906                !!!parse-error (type => 'after head',
4907                                text => $token->{tag_name}, token => $token);
4908                push @{$self->{open_elements}},
4909                    [$self->{head_element}, $el_category->{head}];
4910                $self->{head_element_inserted} = 1;
4911              } else {
4912                !!!cp ('t121');
4913              }
4914    
4915                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
4916                $script_start_tag->($insert_to_current);            $script_start_tag->();
4917                pop @{$self->{open_elements}}            ## ISSUE: A spec bug  [Bug 6038]
4918                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
4919                redo B;                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4920              } elsif ($token->{tag_name} eq 'body' or            next B;
4921                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
4922                     $token->{tag_name} eq 'frameset') {
4923                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4924                    !!!cp ('t122');
4925                  ## As if </noscript>                  ## As if </noscript>
4926                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4927                  !!!parse-error (type => 'in noscript:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript',
4928                                    text => $token->{tag_name}, token => $token);
4929                                    
4930                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
4931                  ## As if </head>                  ## As if </head>
# Line 2816  sub _tree_construction_main ($) { Line 4933  sub _tree_construction_main ($) {
4933                                    
4934                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4935                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4936                    !!!cp ('t124');
4937                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4938                                    
4939                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
4940                  } else {
4941                    !!!cp ('t125');
4942                }                }
4943    
4944                ## "after head" insertion mode                ## "after head" insertion mode
4945                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4946                if ($token->{tag_name} eq 'body') {                if ($token->{tag_name} eq 'body') {
4947                    !!!cp ('t126');
4948                  $self->{insertion_mode} = IN_BODY_IM;                  $self->{insertion_mode} = IN_BODY_IM;
4949                } elsif ($token->{tag_name} eq 'frameset') {                } elsif ($token->{tag_name} eq 'frameset') {
4950                    !!!cp ('t127');
4951                  $self->{insertion_mode} = IN_FRAMESET_IM;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4952                } else {                } else {
4953                  die "$0: tag name: $self->{tag_name}";                  die "$0: tag name: $self->{tag_name}";
4954                }                }
4955                  !!!nack ('t127.1');
4956                !!!next-token;                !!!next-token;
4957                redo B;                next B;
4958              } else {              } else {
4959                  !!!cp ('t128');
4960                #                #
4961              }              }
4962    
4963              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4964                  !!!cp ('t129');
4965                ## As if </noscript>                ## As if </noscript>
4966                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4967                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
4968                                  text => $token->{tag_name}, token => $token);
4969                                
4970                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
4971                ## As if </head>                ## As if </head>
# Line 2847  sub _tree_construction_main ($) { Line 4973  sub _tree_construction_main ($) {
4973    
4974                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4975              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4976                  !!!cp ('t130');
4977                ## As if </head>                ## As if </head>
4978                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4979    
4980                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
4981                } else {
4982                  !!!cp ('t131');
4983              }              }
4984    
4985              ## "after head" insertion mode              ## "after head" insertion mode
4986              ## As if <body>              ## As if <body>
4987              !!!insert-element ('body');              !!!insert-element ('body',, $token);
4988              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
4989              ## reprocess              ## reprocess
4990              redo B;              !!!ack-later;
4991                next B;
4992            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
4993              if ($token->{tag_name} eq 'head') {              if ($token->{tag_name} eq 'head') {
4994                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4995                    !!!cp ('t132');
4996                  ## As if <head>                  ## As if <head>
4997                  !!!create-element ($self->{head_element}, 'head');                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4998                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4999                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  push @{$self->{open_elements}},
5000                        [$self->{head_element}, $el_category->{head}];
5001    
5002                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5003                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5004                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5005                  !!!next-token;                  !!!next-token;
5006                  redo B;                  next B;
5007                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5008                    !!!cp ('t133');
5009                  ## As if </noscript>                  ## As if </noscript>
5010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5011                  !!!parse-error (type => 'in noscript:script');                  !!!parse-error (type => 'in noscript:/',
5012                                    text => 'head', token => $token);
5013                                    
5014                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5015                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5016                  $self->{insertion_mode} = AFTER_HEAD_IM;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5017                  !!!next-token;                  !!!next-token;
5018                  redo B;                  next B;
5019                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5020                    !!!cp ('t134');
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} == AFTER_HEAD_IM) {
5026                    !!!cp ('t134.1');
5027                    !!!parse-error (type => 'unmatched end tag', text => 'head',
5028                                    token => $token);
5029                    ## Ignore the token
5030                    !!!next-token;
5031                    next B;
5032                } else {                } else {
5033                  #                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
5034                }                }
5035              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5036                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5037                    !!!cp ('t136');
5038                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5039                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5040                  !!!next-token;                  !!!next-token;
5041                  redo B;                  next B;
5042                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5043                  !!!parse-error (type => 'unmatched end tag:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
5044                    !!!cp ('t137');
5045                    !!!parse-error (type => 'unmatched end tag',
5046                                    text => 'noscript', token => $token);
5047                  ## Ignore the token ## ISSUE: An issue in the spec.                  ## Ignore the token ## ISSUE: An issue in the spec.
5048                  !!!next-token;                  !!!next-token;
5049                  redo B;                  next B;
5050                } else {                } else {
5051                    !!!cp ('t138');
5052                  #                  #
5053                }                }
5054              } elsif ({              } elsif ({
5055                        body => 1, html => 1,                        body => 1, html => 1,
5056                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5057                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                ## TODO: This branch is entirely redundant.
5058                  ## As if <head>                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5059                  !!!create-element ($self->{head_element}, 'head');                    $self->{insertion_mode} == IN_HEAD_IM or
5060                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5061                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!cp ('t140');
5062                    !!!parse-error (type => 'unmatched end tag',
5063                  $self->{insertion_mode} = IN_HEAD_IM;                                  text => $token->{tag_name}, token => $token);
5064                  ## Reprocess in the "in head" insertion mode...                  ## Ignore the token
5065                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                  !!!next-token;
5066                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  next B;
5067                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5068                    !!!cp ('t140.1');
5069                    !!!parse-error (type => 'unmatched end tag',
5070                                    text => $token->{tag_name}, token => $token);
5071                  ## Ignore the token                  ## Ignore the token
5072                  !!!next-token;                  !!!next-token;
5073                  redo B;                  next B;
5074                  } else {
5075                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5076                }                }
5077                              } elsif ($token->{tag_name} eq 'p') {
5078                #                !!!cp ('t142');
5079              } elsif ({                !!!parse-error (type => 'unmatched end tag',
5080                        p => 1, br => 1,                                text => $token->{tag_name}, token => $token);
5081                       }->{$token->{tag_name}}) {                ## Ignore the token
5082                  !!!next-token;
5083                  next B;
5084                } elsif ($token->{tag_name} eq 'br') {
5085                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5086                  ## As if <head>                  !!!cp ('t142.2');
5087                  !!!create-element ($self->{head_element}, 'head');                  ## (before head) as if <head>, (in head) as if </head>
5088                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5089                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5090                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  $self->{insertion_mode} = AFTER_HEAD_IM;
5091      
5092                    ## Reprocess in the "after head" insertion mode...
5093                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5094                    !!!cp ('t143.2');
5095                    ## As if </head>
5096                    pop @{$self->{open_elements}};
5097                    $self->{insertion_mode} = AFTER_HEAD_IM;
5098      
5099                    ## Reprocess in the "after head" insertion mode...
5100                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5101                    !!!cp ('t143.3');
5102                    ## ISSUE: Two parse errors for <head><noscript></br>
5103                    !!!parse-error (type => 'unmatched end tag',
5104                                    text => 'br', token => $token);
5105                    ## As if </noscript>
5106                    pop @{$self->{open_elements}};
5107                  $self->{insertion_mode} = IN_HEAD_IM;                  $self->{insertion_mode} = IN_HEAD_IM;
5108    
5109                  ## Reprocess in the "in head" insertion mode...                  ## Reprocess in the "in head" insertion mode...
5110                }                  ## As if </head>
5111                    pop @{$self->{open_elements}};
5112                    $self->{insertion_mode} = AFTER_HEAD_IM;
5113    
5114                #                  ## Reprocess in the "after head" insertion mode...
5115              } else {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5116                if ($self->{insertion_mode} == AFTER_HEAD_IM) {                  !!!cp ('t143.4');
5117                  #                  #
5118                } else {                } else {
5119                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5120                }                }
5121    
5122                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5123                  !!!parse-error (type => 'unmatched end tag',
5124                                  text => 'br', token => $token);
5125                  ## Ignore the token
5126                  !!!next-token;
5127                  next B;
5128                } else {
5129                  !!!cp ('t145');
5130                  !!!parse-error (type => 'unmatched end tag',
5131                                  text => $token->{tag_name}, token => $token);
5132                  ## Ignore the token
5133                  !!!next-token;
5134                  next B;
5135              }              }
5136    
5137              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5138                  !!!cp ('t146');
5139                ## As if </noscript>                ## As if </noscript>
5140                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5141                !!!parse-error (type => 'in noscript:/'.$token->{tag_name});                !!!parse-error (type => 'in noscript:/',
5142                                  text => $token->{tag_name}, token => $token);
5143                                
5144                ## Reprocess in the "in head" insertion mode...                ## Reprocess in the "in head" insertion mode...
5145                ## As if </head>                ## As if </head>
# Line 2959  sub _tree_construction_main ($) { Line 5147  sub _tree_construction_main ($) {
5147    
5148                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5149              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5150                  !!!cp ('t147');
5151                ## As if </head>                ## As if </head>
5152                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5153    
5154                ## Reprocess in the "after head" insertion mode...                ## Reprocess in the "after head" insertion mode...
5155              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5156                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  ## ISSUE: This case cannot be reached?
5157                  !!!cp ('t148');
5158                  !!!parse-error (type => 'unmatched end tag',
5159                                  text => $token->{tag_name}, token => $token);
5160                ## Ignore the token ## ISSUE: An issue in the spec.                ## Ignore the token ## ISSUE: An issue in the spec.
5161                !!!next-token;                !!!next-token;
5162                redo B;                next B;
5163                } else {
5164                  !!!cp ('t149');
5165              }              }
5166    
5167              ## "after head" insertion mode              ## "after head" insertion mode
5168              ## As if <body>              ## As if <body>
5169              !!!insert-element ('body');              !!!insert-element ('body',, $token);
5170              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
5171              ## reprocess              ## reprocess
5172              redo B;              next B;
5173            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5174              die "$0: $token->{type}: Unknown token type";          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5175            }            !!!cp ('t149.1');
5176    
5177              ## NOTE: As if <head>
5178              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5179              $self->{open_elements}->[-1]->[0]->append_child
5180                  ($self->{head_element});
5181              #push @{$self->{open_elements}},
5182              #    [$self->{head_element}, $el_category->{head}];
5183              #$self->{insertion_mode} = IN_HEAD_IM;
5184              ## NOTE: Reprocess.
5185    
5186              ## NOTE: As if </head>
5187              #pop @{$self->{open_elements}};
5188              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5189              ## NOTE: Reprocess.
5190              
5191              #
5192            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5193              !!!cp ('t149.2');
5194    
5195              ## NOTE: As if </head>
5196              pop @{$self->{open_elements}};
5197              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5198              ## NOTE: Reprocess.
5199    
5200              #
5201            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5202              !!!cp ('t149.3');
5203    
5204              !!!parse-error (type => 'in noscript:#eof', token => $token);
5205    
5206              ## As if </noscript>
5207              pop @{$self->{open_elements}};
5208              #$self->{insertion_mode} = IN_HEAD_IM;
5209              ## NOTE: Reprocess.
5210    
5211              ## NOTE: As if </head>
5212              pop @{$self->{open_elements}};
5213              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5214              ## NOTE: Reprocess.
5215    
5216            ## ISSUE: An issue in the spec.            #
5217            } else {
5218              !!!cp ('t149.4');
5219              #
5220            }
5221    
5222            ## NOTE: As if <body>
5223            !!!insert-element ('body',, $token);
5224            $self->{insertion_mode} = IN_BODY_IM;
5225            ## NOTE: Reprocess.
5226            next B;
5227          } else {
5228            die "$0: $token->{type}: Unknown token type";
5229          }
5230      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
5231            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
5232                !!!cp ('t150');
5233              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5234              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5235                            
5236              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5237    
5238              !!!next-token;              !!!next-token;
5239              redo B;              next B;
5240            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
5241              if ({              if ({
5242                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
# Line 2997  sub _tree_construction_main ($) { Line 5244  sub _tree_construction_main ($) {
5244                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5245                if ($self->{insertion_mode} == IN_CELL_IM) {                if ($self->{insertion_mode} == IN_CELL_IM) {
5246                  ## have an element in table scope                  ## have an element in table scope
5247                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5248                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5249                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] == TABLE_CELL_EL) {
5250                      $tn = $node->[1];                      !!!cp ('t151');
5251                      last INSCOPE;  
5252                    } elsif ({                      ## Close the cell
5253                              table => 1, html => 1,                      !!!back-token; # <x>
5254                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5255                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5256                    }                                line => $token->{line},
5257                  } # INSCOPE                                column => $token->{column}};
5258                    unless (defined $tn) {                      next B;
5259                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5260                      ## Ignore the token                      !!!cp ('t152');
5261                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5262                      redo B;                      last;
5263                    }                    }
5264                                    }
5265                  ## Close the cell  
5266                  !!!back-token; # <?>                  !!!cp ('t153');
5267                  $token = {type => END_TAG_TOKEN, tag_name => $tn};                  !!!parse-error (type => 'start tag not allowed',
5268                  redo B;                      text => $token->{tag_name}, token => $token);
5269                    ## Ignore the token
5270                    !!!nack ('t153.1');
5271                    !!!next-token;
5272                    next B;
5273                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5274                  !!!parse-error (type => 'not closed:caption');                  !!!parse-error (type => 'not closed', text => 'caption',
5275                                    token => $token);
5276                                    
5277                  ## As if </caption>                  ## NOTE: As if </caption>.
5278                  ## have a table element in table scope                  ## have a table element in table scope
5279                  my $i;                  my $i;
5280                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5281                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5282                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5283                      $i = $_;                      if ($node->[1] == CAPTION_EL) {
5284                      last INSCOPE;                        !!!cp ('t155');
5285                    } elsif ({                        $i = $_;
5286                              table => 1, html => 1,                        last INSCOPE;
5287                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5288                      last INSCOPE;                        !!!cp ('t156');
5289                          last;
5290                        }
5291                    }                    }
5292    
5293                      !!!cp ('t157');
5294                      !!!parse-error (type => 'start tag not allowed',
5295                                      text => $token->{tag_name}, token => $token);
5296                      ## Ignore the token
5297                      !!!nack ('t157.1');
5298                      !!!next-token;
5299                      next B;
5300                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5301                                    
5302                  ## generate implied end tags                  ## generate implied end tags
5303                  if ({                  while ($self->{open_elements}->[-1]->[1]
5304                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5305                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5306                       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;  
5307                  }                  }
5308    
5309                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5310                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5311                      !!!parse-error (type => 'not closed',
5312                                      text => $self->{open_elements}->[-1]->[0]
5313                                          ->manakai_local_name,
5314                                      token => $token);
5315                    } else {
5316                      !!!cp ('t160');
5317                  }                  }
5318                                    
5319                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3069  sub _tree_construction_main ($) { Line 5323  sub _tree_construction_main ($) {
5323                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5324                                    
5325                  ## reprocess                  ## reprocess
5326                  redo B;                  !!!ack-later;
5327                    next B;
5328                } else {                } else {
5329                    !!!cp ('t161');
5330                  #                  #
5331                }                }
5332              } else {              } else {
5333                  !!!cp ('t162');
5334                #                #
5335              }              }
5336            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
# Line 3083  sub _tree_construction_main ($) { Line 5340  sub _tree_construction_main ($) {
5340                  my $i;                  my $i;
5341                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5342                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5343                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5344                        !!!cp ('t163');
5345                      $i = $_;                      $i = $_;
5346                      last INSCOPE;                      last INSCOPE;
5347                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5348                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5349                      last INSCOPE;                      last INSCOPE;
5350                    }                    }
5351                  } # INSCOPE                  } # INSCOPE
5352                    unless (defined $i) {                    unless (defined $i) {
5353                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5354                        !!!parse-error (type => 'unmatched end tag',
5355                                        text => $token->{tag_name},
5356                                        token => $token);
5357                      ## Ignore the token                      ## Ignore the token
5358                      !!!next-token;                      !!!next-token;
5359                      redo B;                      next B;
5360                    }                    }
5361                                    
5362                  ## generate implied end tags                  ## generate implied end tags
5363                  if ({                  while ($self->{open_elements}->[-1]->[1]
5364                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5365                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5366                       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;  
5367                  }                  }
5368                    
5369                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5370                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5371                      !!!cp ('t167');
5372                      !!!parse-error (type => 'not closed',
5373                                      text => $self->{open_elements}->[-1]->[0]
5374                                          ->manakai_local_name,
5375                                      token => $token);
5376                    } else {
5377                      !!!cp ('t168');
5378                  }                  }
5379                                    
5380                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3124  sub _tree_construction_main ($) { Line 5384  sub _tree_construction_main ($) {
5384                  $self->{insertion_mode} = IN_ROW_IM;                  $self->{insertion_mode} = IN_ROW_IM;
5385                                    
5386                  !!!next-token;                  !!!next-token;
5387                  redo B;                  next B;
5388                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5389                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5390                    !!!parse-error (type => 'unmatched end tag',
5391                                    text => $token->{tag_name}, token => $token);
5392                  ## Ignore the token                  ## Ignore the token
5393                  !!!next-token;                  !!!next-token;
5394                  redo B;                  next B;
5395                } else {                } else {
5396                    !!!cp ('t170');
5397                  #                  #
5398                }                }
5399              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5400                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5401                  ## have a table element in table scope                  ## have a table element in table scope
5402                  my $i;                  my $i;
5403                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5404                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5405                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5406                      $i = $_;                      if ($node->[1] == CAPTION_EL) {
5407                      last INSCOPE;                        !!!cp ('t171');
5408                    } elsif ({                        $i = $_;
5409                              table => 1, html => 1,                        last INSCOPE;
5410                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5411                      last INSCOPE;                        !!!cp ('t172');
5412                          last;
5413                        }
5414                    }                    }
5415    
5416                      !!!cp ('t173');
5417                      !!!parse-error (type => 'unmatched end tag',
5418                                      text => $token->{tag_name}, token => $token);
5419                      ## Ignore the token
5420                      !!!next-token;
5421                      next B;
5422                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5423                                    
5424                  ## generate implied end tags                  ## generate implied end tags
5425                  if ({                  while ($self->{open_elements}->[-1]->[1]
5426                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5427                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5428                       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;  
5429                  }                  }
5430                                    
5431                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5432                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5433                      !!!parse-error (type => 'not closed',
5434                                      text => $self->{open_elements}->[-1]->[0]
5435                                          ->manakai_local_name,
5436                                      token => $token);
5437                    } else {
5438                      !!!cp ('t176');
5439                  }                  }
5440                                    
5441                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
# Line 3178  sub _tree_construction_main ($) { Line 5445  sub _tree_construction_main ($) {
5445                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5446                                    
5447                  !!!next-token;                  !!!next-token;
5448                  redo B;                  next B;
5449                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5450                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5451                    !!!parse-error (type => 'unmatched end tag',
5452                                    text => $token->{tag_name}, token => $token);
5453                  ## Ignore the token                  ## Ignore the token
5454                  !!!next-token;                  !!!next-token;
5455                  redo B;                  next B;
5456                } else {                } else {
5457                    !!!cp ('t178');
5458                  #                  #
5459                }                }
5460              } elsif ({              } elsif ({
# Line 3195  sub _tree_construction_main ($) { Line 5465  sub _tree_construction_main ($) {
5465                ## have an element in table scope                ## have an element in table scope
5466                my $i;                my $i;
5467                my $tn;                my $tn;
5468                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5469                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5470                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5471                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5472                    last INSCOPE;                      !!!cp ('t179');
5473                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5474                    $tn = $node->[1];  
5475                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5476                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5477                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5478                            table => 1, html => 1,                                line => $token->{line},
5479                           }->{$node->[1]}) {                                column => $token->{column}};
5480                    last INSCOPE;                      next B;
5481                      } elsif ($node->[1] == TABLE_CELL_EL) {
5482                        !!!cp ('t180');
5483                        $tn = $node->[0]->manakai_local_name;
5484                        ## NOTE: There is exactly one |td| or |th| element
5485                        ## in scope in the stack of open elements by definition.
5486                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5487                        ## ISSUE: Can this be reached?
5488                        !!!cp ('t181');
5489                        last;
5490                      }
5491                  }                  }
5492                } # INSCOPE  
5493                unless (defined $i) {                  !!!cp ('t182');
5494                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5495                        text => $token->{tag_name}, token => $token);
5496                  ## Ignore the token                  ## Ignore the token
5497                  !!!next-token;                  !!!next-token;
5498                  redo B;                  next B;
5499                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => END_TAG_TOKEN, tag_name => $tn};  
               redo B;  
5500              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5501                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5502                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5503                                  token => $token);
5504    
5505                ## As if </caption>                ## As if </caption>
5506                ## have a table element in table scope                ## have a table element in table scope
5507                my $i;                my $i;
5508                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5509                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5510                  if ($node->[1] eq 'caption') {                  if ($node->[1] == CAPTION_EL) {
5511                      !!!cp ('t184');
5512                    $i = $_;                    $i = $_;
5513                    last INSCOPE;                    last INSCOPE;
5514                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5515                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5516                    last INSCOPE;                    last INSCOPE;
5517                  }                  }
5518                } # INSCOPE                } # INSCOPE
5519                unless (defined $i) {                unless (defined $i) {
5520                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5521                    !!!parse-error (type => 'unmatched end tag',
5522                                    text => 'caption', token => $token);
5523                  ## Ignore the token                  ## Ignore the token
5524                  !!!next-token;                  !!!next-token;
5525                  redo B;                  next B;
5526                }                }
5527                                
5528                ## generate implied end tags                ## generate implied end tags
5529                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5530                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5531                     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;  
5532                }                }
5533    
5534                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5535                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5536                    !!!parse-error (type => 'not closed',
5537                                    text => $self->{open_elements}->[-1]->[0]
5538                                        ->manakai_local_name,
5539                                    token => $token);
5540                  } else {
5541                    !!!cp ('t189');
5542                }                }
5543    
5544                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
# Line 3271  sub _tree_construction_main ($) { Line 5548  sub _tree_construction_main ($) {
5548                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
5549    
5550                ## reprocess                ## reprocess
5551                redo B;                next B;
5552              } elsif ({              } elsif ({
5553                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5554                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5555                if ($self->{insertion_mode} & BODY_TABLE_IMS) {                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5556                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t190');
5557                    !!!parse-error (type => 'unmatched end tag',
5558                                    text => $token->{tag_name}, token => $token);
5559                  ## Ignore the token                  ## Ignore the token
5560                  !!!next-token;                  !!!next-token;
5561                  redo B;                  next B;
5562                } else {                } else {
5563                    !!!cp ('t191');
5564                  #                  #
5565                }                }
5566              } elsif ({              } elsif ({
# Line 3288  sub _tree_construction_main ($) { Line 5568  sub _tree_construction_main ($) {
5568                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5569                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5570                       $self->{insertion_mode} == IN_CAPTION_IM) {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5571                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5572                  !!!parse-error (type => 'unmatched end tag',
5573                                  text => $token->{tag_name}, token => $token);
5574                ## Ignore the token                ## Ignore the token
5575                !!!next-token;                !!!next-token;
5576                redo B;                next B;
5577              } else {              } else {
5578                  !!!cp ('t193');
5579                #                #
5580              }              }
5581          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5582            for my $entry (@{$self->{open_elements}}) {
5583              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5584                !!!cp ('t75');
5585                !!!parse-error (type => 'in body:#eof', token => $token);
5586                last;
5587              }
5588            }
5589    
5590            ## Stop parsing.
5591            last B;
5592        } else {        } else {
5593          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
5594        }        }
# Line 3302  sub _tree_construction_main ($) { Line 5596  sub _tree_construction_main ($) {
5596        $insert = $insert_to_current;        $insert = $insert_to_current;
5597        #        #
5598      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
5599            if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
5600              ## NOTE: There are "character in table" code clones.          if (not $open_tables->[-1]->[1] and # tainted
5601              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5602                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5603                                
5604                unless (length $token->{data}) {            unless (length $token->{data}) {
5605                  !!!next-token;              !!!cp ('t194');
5606                  redo B;              !!!next-token;
5607                }              next B;
5608              }            } else {
5609                !!!cp ('t195');
5610              }
5611            }
5612    
5613              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5614    
5615              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5616              ## 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);  
5617                            
5618              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5619                   table => 1, tbody => 1, tfoot => 1,            # MUST
5620                   thead => 1, tr => 1,            my $foster_parent_element;
5621                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5622                # MUST            my $prev_sibling;
5623                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5624                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5625                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5626                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5627                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5628                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5629                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5630                      $foster_parent_element = $parent;                  $prev_sibling = $next_sibling->previous_sibling;
5631                      $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});  
5632                } else {                } else {
5633                  $foster_parent_element->insert_before                  !!!cp ('t197');
5634                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5635                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5636                    #
5637                }                }
5638              } else {                last OE;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5639              }              }
5640              } # OE
5641              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5642              $prev_sibling = $foster_parent_element->last_child
5643                  unless defined $foster_parent_element;
5644              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5645              if (defined $prev_sibling and
5646                  $prev_sibling->node_type == 3) {
5647                !!!cp ('t198');
5648                $prev_sibling->manakai_append_text ($token->{data});
5649              } else {
5650                !!!cp ('t199');
5651                $foster_parent_element->insert_before
5652                    ($self->{document}->create_text_node ($token->{data}),
5653                     $next_sibling);
5654              }
5655              $open_tables->[-1]->[1] = 1; # tainted
5656              $open_tables->[-1]->[2] = 1; # ~node inserted
5657            } else {
5658              ## NOTE: Fragment case or in a foster parent'ed element
5659              ## (e.g. |<table><span>a|).  In fragment case, whether the
5660              ## character is appended to existing node or a new node is
5661              ## created is irrelevant, since the foster parent'ed nodes
5662              ## are discarded and fragment parsing does not invoke any
5663              ## script.
5664              !!!cp ('t200');
5665              $self->{open_elements}->[-1]->[0]->manakai_append_text
5666                  ($token->{data});
5667            }
5668                            
5669              !!!next-token;          !!!next-token;
5670              redo B;          next B;
5671            } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
5672              if ({          if ({
5673                   tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => ($self->{insertion_mode} != IN_ROW_IM),
5674                   th => 1, td => 1,               th => 1, td => 1,
5675                  }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5676                if ($self->{insertion_mode} == IN_TABLE_IM) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5677                  ## Clear back to table context              ## Clear back to table context
5678                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5679                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5680                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5681                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5682                  }              }
5683                                
5684                  !!!insert-element ('tbody');              !!!insert-element ('tbody',, $token);
5685                  $self->{insertion_mode} = IN_TABLE_BODY_IM;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5686                  ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
5687                }            }
5688              
5689                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5690                  unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
5691                    !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t202');
5692                  }                !!!parse-error (type => 'missing start tag:tr', token => $token);
5693                }
5694                                    
5695                  ## Clear back to table body context              ## Clear back to table body context
5696                  while (not {              while (not ($self->{open_elements}->[-1]->[1]
5697                    tbody => 1, tfoot => 1, thead => 1, html => 1,                              & TABLE_ROWS_SCOPING_EL)) {
5698                  }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t203');
5699                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## ISSUE: Can this case be reached?
5700                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5701                  }              }
5702                                    
5703                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
5704                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
5705                    !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t204');
5706                    !!!next-token;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5707                    redo B;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5708                  } else {                !!!nack ('t204');
5709                    !!!insert-element ('tr');                !!!next-token;
5710                    ## reprocess in the "in row" insertion mode                next B;
5711                  }              } else {
5712                }                !!!cp ('t205');
5713                  !!!insert-element ('tr',, $token);
5714                  ## reprocess in the "in row" insertion mode
5715                }
5716              } else {
5717                !!!cp ('t206');
5718              }
5719    
5720                ## Clear back to table row context                ## Clear back to table row context
5721                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5722                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5723                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5724                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5725                }                }
5726                                
5727                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5728                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5729              $self->{insertion_mode} = IN_CELL_IM;
5730    
5731                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5732                                
5733              !!!nack ('t207.1');
5734              !!!next-token;
5735              next B;
5736            } elsif ({
5737                      caption => 1, col => 1, colgroup => 1,
5738                      tbody => 1, tfoot => 1, thead => 1,
5739                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5740                     }->{$token->{tag_name}}) {
5741              if ($self->{insertion_mode} == IN_ROW_IM) {
5742                ## As if </tr>
5743                ## have an element in table scope
5744                my $i;
5745                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5746                  my $node = $self->{open_elements}->[$_];
5747                  if ($node->[1] == TABLE_ROW_EL) {
5748                    !!!cp ('t208');
5749                    $i = $_;
5750                    last INSCOPE;
5751                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5752                    !!!cp ('t209');
5753                    last INSCOPE;
5754                  }
5755                } # INSCOPE
5756                unless (defined $i) {
5757                  !!!cp ('t210');
5758                  ## TODO: This type is wrong.
5759                  !!!parse-error (type => 'unmacthed end tag',
5760                                  text => $token->{tag_name}, token => $token);
5761                  ## Ignore the token
5762                  !!!nack ('t210.1');
5763                !!!next-token;                !!!next-token;
5764                redo B;                next B;
5765              } 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;  
                 }  
5766                                    
5767                  ## Clear back to table row context                  ## Clear back to table row context
5768                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5769                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
5770                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t211');
5771                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this case be reached?
5772                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5773                  }                  }
5774                                    
5775                  pop @{$self->{open_elements}}; # tr                  pop @{$self->{open_elements}}; # tr
5776                  $self->{insertion_mode} = IN_TABLE_BODY_IM;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5777                  if ($token->{tag_name} eq 'tr') {                  if ($token->{tag_name} eq 'tr') {
5778                      !!!cp ('t212');
5779                    ## reprocess                    ## reprocess
5780                    redo B;                    !!!ack-later;
5781                      next B;
5782                  } else {                  } else {
5783                      !!!cp ('t213');
5784                    ## reprocess in the "in table body" insertion mode...                    ## reprocess in the "in table body" insertion mode...
5785                  }                  }
5786                }                }
# Line 3467  sub _tree_construction_main ($) { Line 5790  sub _tree_construction_main ($) {
5790                  my $i;                  my $i;
5791                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5792                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5793                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
5794                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t214');
                       }->{$node->[1]}) {  
5795                      $i = $_;                      $i = $_;
5796                      last INSCOPE;                      last INSCOPE;
5797                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5798                              table => 1, html => 1,                      !!!cp ('t215');
                            }->{$node->[1]}) {  
5799                      last INSCOPE;                      last INSCOPE;
5800                    }                    }
5801                  } # INSCOPE                  } # INSCOPE
5802                  unless (defined $i) {                  unless (defined $i) {
5803                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t216');
5804    ## TODO: This erorr type is wrong.
5805                      !!!parse-error (type => 'unmatched end tag',
5806                                      text => $token->{tag_name}, token => $token);
5807                    ## Ignore the token                    ## Ignore the token
5808                      !!!nack ('t216.1');
5809                    !!!next-token;                    !!!next-token;
5810                    redo B;                    next B;
5811                  }                  }
5812    
5813                  ## Clear back to table body context                  ## Clear back to table body context
5814                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5815                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5816                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5817                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5818                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5819                  }                  }
5820                                    
# Line 3503  sub _tree_construction_main ($) { Line 5828  sub _tree_construction_main ($) {
5828                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5829                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
5830                  ## reprocess in "in table" insertion mode...                  ## reprocess in "in table" insertion mode...
5831                  } else {
5832                    !!!cp ('t218');
5833                }                }
5834    
5835                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
5836                  ## Clear back to table context              ## Clear back to table context
5837                  while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5838                         $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5839                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t219');
5840                    pop @{$self->{open_elements}};                ## ISSUE: Can this state be reached?
5841                  }                pop @{$self->{open_elements}};
5842                                }
5843                  !!!insert-element ('colgroup');              
5844                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              !!!insert-element ('colgroup',, $token);
5845                  ## reprocess              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5846                  redo B;              ## reprocess
5847                } elsif ({              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5848                          caption => 1,              !!!ack-later;
5849                          colgroup => 1,              next B;
5850                          tbody => 1, tfoot => 1, thead => 1,            } elsif ({
5851                         }->{$token->{tag_name}}) {                      caption => 1,
5852                  ## Clear back to table context                      colgroup => 1,
5853                  while ($self->{open_elements}->[-1]->[1] ne 'table' and                      tbody => 1, tfoot => 1, thead => 1,
5854                         $self->{open_elements}->[-1]->[1] ne 'html') {                     }->{$token->{tag_name}}) {
5855                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## Clear back to table context
5856                    while (not ($self->{open_elements}->[-1]->[1]
5857                                    & TABLE_SCOPING_EL)) {
5858                      !!!cp ('t220');
5859                      ## ISSUE: Can this state be reached?
5860                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
5861                  }                  }
5862                                    
5863                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
5864                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
5865                                    
5866                  !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5867                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5868                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
5869                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
5870                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
5871                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
5872                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
5873                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
5874                  !!!next-token;                                        }->{$token->{tag_name}};
5875                  redo B;              !!!next-token;
5876                } else {              !!!nack ('t220.1');
5877                  die "$0: in table: <>: $token->{tag_name}";              next B;
5878                }            } else {
5879                die "$0: in table: <>: $token->{tag_name}";
5880              }
5881              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5882                ## NOTE: There are code clones for this "table in table"                !!!parse-error (type => 'not closed',
5883                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                                text => $self->{open_elements}->[-1]->[0]
5884                                      ->manakai_local_name,
5885                                  token => $token);
5886    
5887                ## As if </table>                ## As if </table>
5888                ## have a table element in table scope                ## have a table element in table scope
5889                my $i;                my $i;
5890                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5891                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5892                  if ($node->[1] eq 'table') {                  if ($node->[1] == TABLE_EL) {
5893                      !!!cp ('t221');
5894                    $i = $_;                    $i = $_;
5895                    last INSCOPE;                    last INSCOPE;
5896                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5897                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5898                    last INSCOPE;                    last INSCOPE;
5899                  }                  }
5900                } # INSCOPE                } # INSCOPE
5901                unless (defined $i) {                unless (defined $i) {
5902                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5903    ## TODO: The following is wrong, maybe.
5904                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5905                                    token => $token);
5906                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5907                    !!!nack ('t223.1');
5908                  !!!next-token;                  !!!next-token;
5909                  redo B;                  next B;
5910                }                }
5911                                
5912    ## TODO: Followings are removed from the latest spec.
5913                ## generate implied end tags                ## generate implied end tags
5914                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5915                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5916                     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;  
5917                }                }
5918    
5919                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5920                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5921                    ## NOTE: |<table><tr><table>|
5922                    !!!parse-error (type => 'not closed',
5923                                    text => $self->{open_elements}->[-1]->[0]
5924                                        ->manakai_local_name,
5925                                    token => $token);
5926                  } else {
5927                    !!!cp ('t226');
5928                }                }
5929    
5930                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5931                  pop @{$open_tables};
5932    
5933                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5934    
5935                ## reprocess            ## reprocess
5936                redo B;            !!!ack-later;
5937              next B;
5938            } elsif ($token->{tag_name} eq 'style') {
5939              if (not $open_tables->[-1]->[1]) { # tainted
5940                !!!cp ('t227.8');
5941                ## NOTE: This is a "as if in head" code clone.
5942                $parse_rcdata->(CDATA_CONTENT_MODEL);
5943                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5944                next B;
5945              } else {
5946                !!!cp ('t227.7');
5947                #
5948              }
5949            } elsif ($token->{tag_name} eq 'script') {
5950              if (not $open_tables->[-1]->[1]) { # tainted
5951                !!!cp ('t227.6');
5952                ## NOTE: This is a "as if in head" code clone.
5953                $script_start_tag->();
5954                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5955                next B;
5956              } else {
5957                !!!cp ('t227.5');
5958                #
5959              }
5960            } elsif ($token->{tag_name} eq 'input') {
5961              if (not $open_tables->[-1]->[1]) { # tainted
5962                if ($token->{attributes}->{type}) { ## TODO: case
5963                  my $type = lc $token->{attributes}->{type}->{value};
5964                  if ($type eq 'hidden') {
5965                    !!!cp ('t227.3');
5966                    !!!parse-error (type => 'in table',
5967                                    text => $token->{tag_name}, token => $token);
5968    
5969                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5970                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5971    
5972                    ## TODO: form element pointer
5973    
5974                    pop @{$self->{open_elements}};
5975    
5976                    !!!next-token;
5977                    !!!ack ('t227.2.1');
5978                    next B;
5979                  } else {
5980                    !!!cp ('t227.2');
5981                    #
5982                  }
5983              } else {              } else {
5984                  !!!cp ('t227.1');
5985                #                #
5986              }              }
5987            } elsif ($token->{type} == END_TAG_TOKEN) {            } else {
5988                !!!cp ('t227.4');
5989                #
5990              }
5991            } else {
5992              !!!cp ('t227');
5993              #
5994            }
5995    
5996            !!!parse-error (type => 'in table', text => $token->{tag_name},
5997                            token => $token);
5998    
5999            $insert = $insert_to_foster;
6000            #
6001          } elsif ($token->{type} == END_TAG_TOKEN) {
6002              if ($token->{tag_name} eq 'tr' and              if ($token->{tag_name} eq 'tr' and
6003                  $self->{insertion_mode} == IN_ROW_IM) {                  $self->{insertion_mode} == IN_ROW_IM) {
6004                ## have an element in table scope                ## have an element in table scope
6005                my $i;                my $i;
6006                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6007                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6008                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_ROW_EL) {
6009                      !!!cp ('t228');
6010                    $i = $_;                    $i = $_;
6011                    last INSCOPE;                    last INSCOPE;
6012                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6013                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
6014                    last INSCOPE;                    last INSCOPE;
6015                  }                  }
6016                } # INSCOPE                } # INSCOPE
6017                unless (defined $i) {                unless (defined $i) {
6018                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t230');
6019                    !!!parse-error (type => 'unmatched end tag',
6020                                    text => $token->{tag_name}, token => $token);
6021                  ## Ignore the token                  ## Ignore the token
6022                    !!!nack ('t230.1');
6023                  !!!next-token;                  !!!next-token;
6024                  redo B;                  next B;
6025                  } else {
6026                    !!!cp ('t232');
6027                }                }
6028    
6029                ## Clear back to table row context                ## Clear back to table row context
6030                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6031                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6032                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6033                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6034                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6035                }                }
6036    
6037                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6038                $self->{insertion_mode} = IN_TABLE_BODY_IM;                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6039                !!!next-token;                !!!next-token;
6040                redo B;                !!!nack ('t231.1');
6041                  next B;
6042              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6043                if ($self->{insertion_mode} == IN_ROW_IM) {                if ($self->{insertion_mode} == IN_ROW_IM) {
6044                  ## As if </tr>                  ## As if </tr>
# Line 3639  sub _tree_construction_main ($) { Line 6046  sub _tree_construction_main ($) {
6046                  my $i;                  my $i;
6047                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6048                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6049                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6050                        !!!cp ('t233');
6051                      $i = $_;                      $i = $_;
6052                      last INSCOPE;                      last INSCOPE;
6053                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6054                              table => 1, html => 1,                      !!!cp ('t234');
                            }->{$node->[1]}) {  
6055                      last INSCOPE;                      last INSCOPE;
6056                    }                    }
6057                  } # INSCOPE                  } # INSCOPE
6058                  unless (defined $i) {                  unless (defined $i) {
6059                    !!!parse-error (type => 'unmatched end tag:'.$token->{type});                    !!!cp ('t235');
6060    ## TODO: The following is wrong.
6061                      !!!parse-error (type => 'unmatched end tag',
6062                                      text => $token->{type}, token => $token);
6063                    ## Ignore the token                    ## Ignore the token
6064                      !!!nack ('t236.1');
6065                    !!!next-token;                    !!!next-token;
6066                    redo B;                    next B;
6067                  }                  }
6068                                    
6069                  ## Clear back to table row context                  ## Clear back to table row context
6070                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6071                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6072                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t236');
6073                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6074                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6075                  }                  }
6076                                    
# Line 3673  sub _tree_construction_main ($) { Line 6084  sub _tree_construction_main ($) {
6084                  my $i;                  my $i;
6085                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6086                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6087                    if ({                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
6088                         tbody => 1, thead => 1, tfoot => 1,                      !!!cp ('t237');
                       }->{$node->[1]}) {  
6089                      $i = $_;                      $i = $_;
6090                      last INSCOPE;                      last INSCOPE;
6091                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6092                              table => 1, html => 1,                      !!!cp ('t238');
                            }->{$node->[1]}) {  
6093                      last INSCOPE;                      last INSCOPE;
6094                    }                    }
6095                  } # INSCOPE                  } # INSCOPE
6096                  unless (defined $i) {                  unless (defined $i) {
6097                    !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    !!!cp ('t239');
6098                      !!!parse-error (type => 'unmatched end tag',
6099                                      text => $token->{tag_name}, token => $token);
6100                    ## Ignore the token                    ## Ignore the token
6101                      !!!nack ('t239.1');
6102                    !!!next-token;                    !!!next-token;
6103                    redo B;                    next B;
6104                  }                  }
6105                                    
6106                  ## Clear back to table body context                  ## Clear back to table body context
6107                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6108                    tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
6109                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t240');
                   !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6110                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6111                  }                  }
6112                                    
# Line 3711  sub _tree_construction_main ($) { Line 6122  sub _tree_construction_main ($) {
6122                  ## reprocess in the "in table" insertion mode...                  ## reprocess in the "in table" insertion mode...
6123                }                }
6124    
6125                  ## NOTE: </table> in the "in table" insertion mode.
6126                  ## When you edit the code fragment below, please ensure that
6127                  ## the code for <table> in the "in table" insertion mode
6128                  ## is synced with it.
6129    
6130                ## have a table element in table scope                ## have a table element in table scope
6131                my $i;                my $i;
6132                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6133                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6134                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_EL) {
6135                      !!!cp ('t241');
6136                    $i = $_;                    $i = $_;
6137                    last INSCOPE;                    last INSCOPE;
6138                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6139                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6140                    last INSCOPE;                    last INSCOPE;
6141                  }                  }
6142                } # INSCOPE                } # INSCOPE
6143                unless (defined $i) {                unless (defined $i) {
6144                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6145                    !!!parse-error (type => 'unmatched end tag',
6146                                    text => $token->{tag_name}, token => $token);
6147                  ## Ignore the token                  ## Ignore the token
6148                    !!!nack ('t243.1');
6149                  !!!next-token;                  !!!next-token;
6150                  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]);  
6151                }                }
6152                                    
6153                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
6154                  pop @{$open_tables};
6155                                
6156                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
6157                                
6158                !!!next-token;                !!!next-token;
6159                redo B;                next B;
6160              } elsif ({              } elsif ({
6161                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6162                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
# Line 3762  sub _tree_construction_main ($) { Line 6166  sub _tree_construction_main ($) {
6166                  my $i;                  my $i;
6167                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6168                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6169                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6170                        !!!cp ('t247');
6171                      $i = $_;                      $i = $_;
6172                      last INSCOPE;                      last INSCOPE;
6173                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6174                              table => 1, html => 1,                      !!!cp ('t248');
                            }->{$node->[1]}) {  
6175                      last INSCOPE;                      last INSCOPE;
6176                    }                    }
6177                  } # INSCOPE                  } # INSCOPE
6178                    unless (defined $i) {                    unless (defined $i) {
6179                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t249');
6180                        !!!parse-error (type => 'unmatched end tag',
6181                                        text => $token->{tag_name}, token => $token);
6182                      ## Ignore the token                      ## Ignore the token
6183                        !!!nack ('t249.1');
6184                      !!!next-token;                      !!!next-token;
6185                      redo B;                      next B;
6186                    }                    }
6187                                    
6188                  ## As if </tr>                  ## As if </tr>
# Line 3783  sub _tree_construction_main ($) { Line 6190  sub _tree_construction_main ($) {
6190                  my $i;                  my $i;
6191                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6192                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
6193                    if ($node->[1] eq 'tr') {                    if ($node->[1] == TABLE_ROW_EL) {
6194                        !!!cp ('t250');
6195                      $i = $_;                      $i = $_;
6196                      last INSCOPE;                      last INSCOPE;
6197                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6198                              table => 1, html => 1,                      !!!cp ('t251');
                            }->{$node->[1]}) {  
6199                      last INSCOPE;                      last INSCOPE;
6200                    }                    }
6201                  } # INSCOPE                  } # INSCOPE
6202                    unless (defined $i) {                    unless (defined $i) {
6203                      !!!parse-error (type => 'unmatched end tag:tr');                      !!!cp ('t252');
6204                        !!!parse-error (type => 'unmatched end tag',
6205                                        text => 'tr', token => $token);
6206                      ## Ignore the token                      ## Ignore the token
6207                        !!!nack ('t252.1');
6208                      !!!next-token;                      !!!next-token;
6209                      redo B;                      next B;
6210                    }                    }
6211                                    
6212                  ## Clear back to table row context                  ## Clear back to table row context
6213                  while (not {                  while (not ($self->{open_elements}->[-1]->[1]
6214                    tr => 1, html => 1,                                  & TABLE_ROW_SCOPING_EL)) {
6215                  }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t253');
6216                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6217                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
6218                  }                  }
6219                                    
# Line 3816  sub _tree_construction_main ($) { Line 6226  sub _tree_construction_main ($) {
6226                my $i;                my $i;
6227                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6228                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6229                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6230                      !!!cp ('t254');
6231                    $i = $_;                    $i = $_;
6232                    last INSCOPE;                    last INSCOPE;
6233                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6234                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6235                    last INSCOPE;                    last INSCOPE;
6236                  }                  }
6237                } # INSCOPE                } # INSCOPE
6238                unless (defined $i) {                unless (defined $i) {
6239                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t256');
6240                    !!!parse-error (type => 'unmatched end tag',
6241                                    text => $token->{tag_name}, token => $token);
6242                  ## Ignore the token                  ## Ignore the token
6243                    !!!nack ('t256.1');
6244                  !!!next-token;                  !!!next-token;
6245                  redo B;                  next B;
6246                }                }
6247    
6248                ## Clear back to table body context                ## Clear back to table body context
6249                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6250                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6251                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6252                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6253                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6254                }                }
6255    
6256                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6257                $self->{insertion_mode} = IN_TABLE_IM;                $self->{insertion_mode} = IN_TABLE_IM;
6258                  !!!nack ('t257.1');
6259                !!!next-token;                !!!next-token;
6260                redo B;                next B;
6261              } elsif ({              } elsif ({
6262                        body => 1, caption => 1, col => 1, colgroup => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6263                        html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6264                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM                        tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6265                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM                        tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6266                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6267                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6268                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6269                !!!next-token;                            text => $token->{tag_name}, token => $token);
6270                redo B;            ## Ignore the token
6271              } else {            !!!nack ('t258.1');
6272                #             !!!next-token;
6273              }            next B;
6274            } else {          } else {
6275              die "$0: $token->{type}: Unknown token type";            !!!cp ('t259');
6276            }            !!!parse-error (type => 'in table:/',
6277                              text => $token->{tag_name}, token => $token);
6278    
6279        !!!parse-error (type => 'in table:'.$token->{tag_name});            $insert = $insert_to_foster;
6280              #
6281            }
6282          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6283            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6284                    @{$self->{open_elements}} == 1) { # redundant, maybe
6285              !!!parse-error (type => 'in body:#eof', token => $token);
6286              !!!cp ('t259.1');
6287              #
6288            } else {
6289              !!!cp ('t259.2');
6290              #
6291            }
6292    
6293        $insert = $insert_to_foster;          ## Stop parsing
6294        #          last B;
6295          } else {
6296            die "$0: $token->{type}: Unknown token type";
6297          }
6298      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6299            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
6300              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6301                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6302                unless (length $token->{data}) {                unless (length $token->{data}) {
6303                    !!!cp ('t260');
6304                  !!!next-token;                  !!!next-token;
6305                  redo B;                  next B;
6306                }                }
6307              }              }
6308                            
6309                !!!cp ('t261');
6310              #              #
6311            } elsif ($token->{type} == START_TAG_TOKEN) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6312              if ($token->{tag_name} eq 'col') {              if ($token->{tag_name} eq 'col') {
6313                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t262');
6314                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6315                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
6316                  !!!ack ('t262.1');
6317                !!!next-token;                !!!next-token;
6318                redo B;                next B;
6319              } else {              } else {
6320                  !!!cp ('t263');
6321                #                #
6322              }              }
6323            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
6324              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
6325                if ($self->{open_elements}->[-1]->[1] eq 'html') {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6326                  !!!parse-error (type => 'unmatched end tag:colgroup');                  !!!cp ('t264');
6327                    !!!parse-error (type => 'unmatched end tag',
6328                                    text => 'colgroup', token => $token);
6329                  ## Ignore the token                  ## Ignore the token
6330                  !!!next-token;                  !!!next-token;
6331                  redo B;                  next B;
6332                } else {                } else {
6333                    !!!cp ('t265');
6334                  pop @{$self->{open_elements}}; # colgroup                  pop @{$self->{open_elements}}; # colgroup
6335                  $self->{insertion_mode} = IN_TABLE_IM;                  $self->{insertion_mode} = IN_TABLE_IM;
6336                  !!!next-token;                  !!!next-token;
6337                  redo B;                              next B;            
6338                }                }
6339              } elsif ($token->{tag_name} eq 'col') {              } elsif ($token->{tag_name} eq 'col') {
6340                !!!parse-error (type => 'unmatched end tag:col');                !!!cp ('t266');
6341                  !!!parse-error (type => 'unmatched end tag',
6342                                  text => 'col', token => $token);
6343                ## Ignore the token                ## Ignore the token
6344                !!!next-token;                !!!next-token;
6345                redo B;                next B;
6346              } else {              } else {
6347                  !!!cp ('t267');
6348                #                #
6349              }              }
6350            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6351              #          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6352            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6353              !!!cp ('t270.2');
6354              ## Stop parsing.
6355              last B;
6356            } else {
6357              ## NOTE: As if </colgroup>.
6358              !!!cp ('t270.1');
6359              pop @{$self->{open_elements}}; # colgroup
6360              $self->{insertion_mode} = IN_TABLE_IM;
6361              ## Reprocess.
6362              next B;
6363            }
6364          } else {
6365            die "$0: $token->{type}: Unknown token type";
6366          }
6367    
6368            ## As if </colgroup>            ## As if </colgroup>
6369            if ($self->{open_elements}->[-1]->[1] eq 'html') {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6370              !!!parse-error (type => 'unmatched end tag:colgroup');              !!!cp ('t269');
6371    ## TODO: Wrong error type?
6372                !!!parse-error (type => 'unmatched end tag',
6373                                text => 'colgroup', token => $token);
6374              ## Ignore the token              ## Ignore the token
6375                !!!nack ('t269.1');
6376              !!!next-token;              !!!next-token;
6377              redo B;              next B;
6378            } else {            } else {
6379                !!!cp ('t270');
6380              pop @{$self->{open_elements}}; # colgroup              pop @{$self->{open_elements}}; # colgroup
6381              $self->{insertion_mode} = IN_TABLE_IM;              $self->{insertion_mode} = IN_TABLE_IM;
6382                !!!ack-later;
6383              ## reprocess              ## reprocess
6384              redo B;              next B;
6385              }
6386        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6387          if ($token->{type} == CHARACTER_TOKEN) {
6388            !!!cp ('t271');
6389            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6390            !!!next-token;
6391            next B;
6392          } elsif ($token->{type} == START_TAG_TOKEN) {
6393            if ($token->{tag_name} eq 'option') {
6394              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6395                !!!cp ('t272');
6396                ## As if </option>
6397                pop @{$self->{open_elements}};
6398              } else {
6399                !!!cp ('t273');
6400            }            }
     } elsif ($self->{insertion_mode} == IN_SELECT_IM) {  
           if ($token->{type} == CHARACTER_TOKEN) {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} == START_TAG_TOKEN) {  
             if ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @{$self->{open_elements}};  
               }  
6401    
6402                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6403                !!!next-token;            !!!nack ('t273.1');
6404                redo B;            !!!next-token;
6405              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6406                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6407                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6408                  pop @{$self->{open_elements}};              !!!cp ('t274');
6409                }              ## As if </option>
6410                pop @{$self->{open_elements}};
6411              } else {
6412                !!!cp ('t275');
6413              }
6414    
6415                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6416                  ## As if </optgroup>              !!!cp ('t276');
6417                  pop @{$self->{open_elements}};              ## As if </optgroup>
6418                }              pop @{$self->{open_elements}};
6419              } else {
6420                !!!cp ('t277');
6421              }
6422    
6423                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6424                !!!next-token;            !!!nack ('t277.1');
6425                redo B;            !!!next-token;
6426              } elsif ($token->{tag_name} eq 'select') {            next B;
6427                !!!parse-error (type => 'not closed:select');          } elsif ({
6428                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6429                ## have an element in table scope                   }->{$token->{tag_name}} or
6430                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6431                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6432                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6433                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6434                    $i = $_;                     tr => 1, td => 1, th => 1,
6435                    last INSCOPE;                    }->{$token->{tag_name}})) {
6436                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6437                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6438                           }->{$node->[1]}) {                            token => $token);
6439                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6440                  }            ## as if there were </select> (otherwise).
6441                } # INSCOPE            ## have an element in table scope
6442                unless (defined $i) {            my $i;
6443                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6444                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6445                  !!!next-token;              if ($node->[1] == SELECT_EL) {
6446                  redo B;                !!!cp ('t278');
6447                }                $i = $_;
6448                  last INSCOPE;
6449                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6450                  !!!cp ('t279');
6451                  last INSCOPE;
6452                }
6453              } # INSCOPE
6454              unless (defined $i) {
6455                !!!cp ('t280');
6456                !!!parse-error (type => 'unmatched end tag',
6457                                text => 'select', token => $token);
6458                ## Ignore the token
6459                !!!nack ('t280.1');
6460                !!!next-token;
6461                next B;
6462              }
6463                                
6464                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6465              splice @{$self->{open_elements}}, $i;
6466    
6467                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6468    
6469                !!!next-token;            if ($token->{tag_name} eq 'select') {
6470                redo B;              !!!nack ('t281.2');
6471              } else {              !!!next-token;
6472                #              next B;
6473              } else {
6474                !!!cp ('t281.1');
6475                !!!ack-later;
6476                ## Reprocess the token.
6477                next B;
6478              }
6479            } else {
6480              !!!cp ('t282');
6481              !!!parse-error (type => 'in select',
6482                              text => $token->{tag_name}, token => $token);
6483              ## Ignore the token
6484              !!!nack ('t282.1');
6485              !!!next-token;
6486              next B;
6487            }
6488          } elsif ($token->{type} == END_TAG_TOKEN) {
6489            if ($token->{tag_name} eq 'optgroup') {
6490              if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6491                  $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6492                !!!cp ('t283');
6493                ## As if </option>
6494                splice @{$self->{open_elements}}, -2;
6495              } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6496                !!!cp ('t284');
6497                pop @{$self->{open_elements}};
6498              } else {
6499                !!!cp ('t285');
6500                !!!parse-error (type => 'unmatched end tag',
6501                                text => $token->{tag_name}, token => $token);
6502                ## Ignore the token
6503              }
6504              !!!nack ('t285.1');
6505              !!!next-token;
6506              next B;
6507            } elsif ($token->{tag_name} eq 'option') {
6508              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6509                !!!cp ('t286');
6510                pop @{$self->{open_elements}};
6511              } else {
6512                !!!cp ('t287');
6513                !!!parse-error (type => 'unmatched end tag',
6514                                text => $token->{tag_name}, token => $token);
6515                ## Ignore the token
6516              }
6517              !!!nack ('t287.1');
6518              !!!next-token;
6519              next B;
6520            } elsif ($token->{tag_name} eq 'select') {
6521              ## have an element in table scope
6522              my $i;
6523              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6524                my $node = $self->{open_elements}->[$_];
6525                if ($node->[1] == SELECT_EL) {
6526                  !!!cp ('t288');
6527                  $i = $_;
6528                  last INSCOPE;
6529                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6530                  !!!cp ('t289');
6531                  last INSCOPE;
6532              }              }
6533            } elsif ($token->{type} == END_TAG_TOKEN) {            } # INSCOPE
6534              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6535                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
6536                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6537                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6538                  splice @{$self->{open_elements}}, -2;              ## Ignore the token
6539                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6540                  pop @{$self->{open_elements}};              !!!next-token;
6541                } else {              next B;
6542                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            }
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6543                                
6544                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6545              splice @{$self->{open_elements}}, $i;
6546    
6547                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6548    
6549                !!!next-token;            !!!nack ('t291.1');
6550                redo B;            !!!next-token;
6551              } elsif ({            next B;
6552                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6553                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6554                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6555                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6556                                   }->{$token->{tag_name}}) {
6557                ## have an element in table scope  ## TODO: The following is wrong?
6558                my $i;            !!!parse-error (type => 'unmatched end tag',
6559                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6560                                
6561                ## As if </select>            ## have an element in table scope
6562                ## have an element in table scope            my $i;
6563                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6564                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
6565                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6566                  if ($node->[1] eq 'select') {                !!!cp ('t292');
6567                    $i = $_;                $i = $_;
6568                    last INSCOPE;                last INSCOPE;
6569                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6570                            table => 1, html => 1,                !!!cp ('t293');
6571                           }->{$node->[1]}) {                last INSCOPE;
6572                    last INSCOPE;              }
6573                  }            } # INSCOPE
6574                } # INSCOPE            unless (defined $i) {
6575                unless (defined $i) {              !!!cp ('t294');
6576                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6577                  ## Ignore the </select> token              !!!nack ('t294.1');
6578                  !!!next-token; ## TODO: ok?              !!!next-token;
6579                  redo B;              next B;
6580                }            }
6581                                
6582                splice @{$self->{open_elements}}, $i;            ## As if </select>
6583              ## have an element in table scope
6584                $self->_reset_insertion_mode;            undef $i;
6585              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6586                ## reprocess              my $node = $self->{open_elements}->[$_];
6587                redo B;              if ($node->[1] == SELECT_EL) {
6588              } else {                !!!cp ('t295');
6589                #                $i = $_;
6590                  last INSCOPE;
6591                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6592    ## ISSUE: Can this state be reached?
6593                  !!!cp ('t296');
6594                  last INSCOPE;
6595              }              }
6596            } else {            } # INSCOPE
6597              #            unless (defined $i) {
6598                !!!cp ('t297');
6599    ## TODO: The following error type is correct?
6600                !!!parse-error (type => 'unmatched end tag',
6601                                text => 'select', token => $token);
6602                ## Ignore the </select> token
6603                !!!nack ('t297.1');
6604                !!!next-token; ## TODO: ok?
6605                next B;
6606            }            }
6607                  
6608              !!!cp ('t298');
6609              splice @{$self->{open_elements}}, $i;
6610    
6611              $self->_reset_insertion_mode;
6612    
6613            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!ack-later;
6614              ## reprocess
6615              next B;
6616            } else {
6617              !!!cp ('t299');
6618              !!!parse-error (type => 'in select:/',
6619                              text => $token->{tag_name}, token => $token);
6620            ## Ignore the token            ## Ignore the token
6621              !!!nack ('t299.3');
6622            !!!next-token;            !!!next-token;
6623            redo B;            next B;
6624            }
6625          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6626            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6627                    @{$self->{open_elements}} == 1) { # redundant, maybe
6628              !!!cp ('t299.1');
6629              !!!parse-error (type => 'in body:#eof', token => $token);
6630            } else {
6631              !!!cp ('t299.2');
6632            }
6633    
6634            ## Stop parsing.
6635            last B;
6636          } else {
6637            die "$0: $token->{type}: Unknown token type";
6638          }
6639      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {      } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6640        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6641          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6642            my $data = $1;            my $data = $1;
6643            ## As if in body            ## As if in body
6644            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
# Line 4106  sub _tree_construction_main ($) { Line 6646  sub _tree_construction_main ($) {
6646            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6647                        
6648            unless (length $token->{data}) {            unless (length $token->{data}) {
6649                !!!cp ('t300');
6650              !!!next-token;              !!!next-token;
6651              redo B;              next B;
6652            }            }
6653          }          }
6654                    
6655          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6656            !!!parse-error (type => 'after html:#character');            !!!cp ('t301');
6657              !!!parse-error (type => 'after html:#text', token => $token);
6658            ## Reprocess in the "main" phase, "after body" insertion mode...            #
6659            } else {
6660              !!!cp ('t302');
6661              ## "after body" insertion mode
6662              !!!parse-error (type => 'after body:#text', token => $token);
6663              #
6664          }          }
           
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:#character');  
6665    
6666          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6667          ## reprocess          ## reprocess
6668          redo B;          next B;
6669        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
6670          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6671            !!!parse-error (type => 'after html:'.$token->{tag_name});            !!!cp ('t303');
6672                        !!!parse-error (type => 'after html',
6673            ## Reprocess in the "main" phase, "after body" insertion mode...                            text => $token->{tag_name}, token => $token);
6674              #
6675            } else {
6676              !!!cp ('t304');
6677              ## "after body" insertion mode
6678              !!!parse-error (type => 'after body',
6679                              text => $token->{tag_name}, token => $token);
6680              #
6681          }          }
6682    
         ## "after body" insertion mode  
         !!!parse-error (type => 'after body:'.$token->{tag_name});  
   
6683          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
6684            !!!ack-later;
6685          ## reprocess          ## reprocess
6686          redo B;          next B;
6687        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6688          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6689            !!!parse-error (type => 'after html:/'.$token->{tag_name});            !!!cp ('t305');
6690              !!!parse-error (type => 'after html:/',
6691                              text => $token->{tag_name}, token => $token);
6692                        
6693            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6694            ## Reprocess in the "main" phase, "after body" insertion mode...            ## Reprocess.
6695              next B;
6696            } else {
6697              !!!cp ('t306');
6698          }          }
6699    
6700          ## "after body" insertion mode          ## "after body" insertion mode
6701          if ($token->{tag_name} eq 'html') {          if ($token->{tag_name} eq 'html') {
6702            if (defined $self->{inner_html_node}) {            if (defined $self->{inner_html_node}) {
6703              !!!parse-error (type => 'unmatched end tag:html');              !!!cp ('t307');
6704                !!!parse-error (type => 'unmatched end tag',
6705                                text => 'html', token => $token);
6706              ## Ignore the token              ## Ignore the token
6707              !!!next-token;              !!!next-token;
6708              redo B;              next B;
6709            } else {            } else {
6710                !!!cp ('t308');
6711              $self->{insertion_mode} = AFTER_HTML_BODY_IM;              $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6712              !!!next-token;              !!!next-token;
6713              redo B;              next B;
6714            }            }
6715          } else {          } else {
6716            !!!parse-error (type => 'after body:/'.$token->{tag_name});            !!!cp ('t309');
6717              !!!parse-error (type => 'after body:/',
6718                              text => $token->{tag_name}, token => $token);
6719    
6720            $self->{insertion_mode} = IN_BODY_IM;            $self->{insertion_mode} = IN_BODY_IM;
6721            ## reprocess            ## reprocess
6722            redo B;            next B;
6723          }          }
6724          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6725            !!!cp ('t309.2');
6726            ## Stop parsing
6727            last B;
6728        } else {        } else {
6729          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6730        }        }
6731      } elsif ($self->{insertion_mode} & FRAME_IMS) {      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6732        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
6733          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6734            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6735                        
6736            unless (length $token->{data}) {            unless (length $token->{data}) {
6737                !!!cp ('t310');
6738              !!!next-token;              !!!next-token;
6739              redo B;              next B;
6740            }            }
6741          }          }
6742                    
6743          if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6744            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6745              !!!parse-error (type => 'in frameset:#character');              !!!cp ('t311');
6746                !!!parse-error (type => 'in frameset:#text', token => $token);
6747            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6748              !!!parse-error (type => 'after frameset:#character');              !!!cp ('t312');
6749            } else { # "after html frameset"              !!!parse-error (type => 'after frameset:#text', token => $token);
6750              !!!parse-error (type => 'after html:#character');            } else { # "after after frameset"
6751                !!!cp ('t313');
6752              $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');  
6753            }            }
6754                        
6755            ## Ignore the token.            ## Ignore the token.
6756            if (length $token->{data}) {            if (length $token->{data}) {
6757                !!!cp ('t314');
6758              ## reprocess the rest of characters              ## reprocess the rest of characters
6759            } else {            } else {
6760                !!!cp ('t315');
6761              !!!next-token;              !!!next-token;
6762            }            }
6763            redo B;            next B;
6764          }          }
6765                    
6766          die qq[$0: Character "$token->{data}"];          die qq[$0: Character "$token->{data}"];
6767        } 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...  
         }  
   
6768          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6769              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6770            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6771              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6772              !!!nack ('t318.1');
6773            !!!next-token;            !!!next-token;
6774            redo B;            next B;
6775          } elsif ($token->{tag_name} eq 'frame' and          } elsif ($token->{tag_name} eq 'frame' and
6776                   $self->{insertion_mode} == IN_FRAMESET_IM) {                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6777            !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t319');
6778              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6779            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6780              !!!ack ('t319.1');
6781            !!!next-token;            !!!next-token;
6782            redo B;            next B;
6783          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6784            ## NOTE: As if in body.            !!!cp ('t320');
6785            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6786            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6787              next B;
6788    
6789              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6790              ## has no parse error.
6791          } else {          } else {
6792            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6793              !!!parse-error (type => 'in frameset:'.$token->{tag_name});              !!!cp ('t321');
6794            } else {              !!!parse-error (type => 'in frameset',
6795              !!!parse-error (type => 'after frameset:'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6796              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6797                !!!cp ('t322');
6798                !!!parse-error (type => 'after frameset',
6799                                text => $token->{tag_name}, token => $token);
6800              } else { # "after after frameset"
6801                !!!cp ('t322.2');
6802                !!!parse-error (type => 'after after frameset',
6803                                text => $token->{tag_name}, token => $token);
6804            }            }
6805            ## Ignore the token            ## Ignore the token
6806              !!!nack ('t322.1');
6807            !!!next-token;            !!!next-token;
6808            redo B;            next B;
6809          }          }
6810        } 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...  
         }  
   
6811          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
6812              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
6813            if ($self->{open_elements}->[-1]->[1] eq 'html' and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6814                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6815              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6816                !!!parse-error (type => 'unmatched end tag',
6817                                text => $token->{tag_name}, token => $token);
6818              ## Ignore the token              ## Ignore the token
6819              !!!next-token;              !!!next-token;
6820            } else {            } else {
6821                !!!cp ('t326');
6822              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6823              !!!next-token;              !!!next-token;
6824            }            }
6825    
6826            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6827                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6828                !!!cp ('t327');
6829              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6830              } else {
6831                !!!cp ('t328');
6832            }            }
6833            redo B;            next B;
6834          } elsif ($token->{tag_name} eq 'html' and          } elsif ($token->{tag_name} eq 'html' and
6835                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6836              !!!cp ('t329');
6837            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6838            !!!next-token;            !!!next-token;
6839            redo B;            next B;
6840          } else {          } else {
6841            if ($self->{insertion_mode} == IN_FRAMESET_IM) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6842              !!!parse-error (type => 'in frameset:/'.$token->{tag_name});              !!!cp ('t330');
6843            } else {              !!!parse-error (type => 'in frameset:/',
6844              !!!parse-error (type => 'after frameset:/'.$token->{tag_name});                              text => $token->{tag_name}, token => $token);
6845              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6846                !!!cp ('t330.1');
6847                !!!parse-error (type => 'after frameset:/',
6848                                text => $token->{tag_name}, token => $token);
6849              } else { # "after after html"
6850                !!!cp ('t331');
6851                !!!parse-error (type => 'after after frameset:/',
6852                                text => $token->{tag_name}, token => $token);
6853            }            }
6854            ## Ignore the token            ## Ignore the token
6855            !!!next-token;            !!!next-token;
6856            redo B;            next B;
6857            }
6858          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6859            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6860                    @{$self->{open_elements}} == 1) { # redundant, maybe
6861              !!!cp ('t331.1');
6862              !!!parse-error (type => 'in body:#eof', token => $token);
6863            } else {
6864              !!!cp ('t331.2');
6865          }          }
6866            
6867            ## Stop parsing
6868            last B;
6869        } else {        } else {
6870          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6871        }        }
   
       ## ISSUE: An issue in spec here  
6872      } else {      } else {
6873        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6874      }      }
# Line 4285  sub _tree_construction_main ($) { Line 6876  sub _tree_construction_main ($) {
6876      ## "in body" insertion mode      ## "in body" insertion mode
6877      if ($token->{type} == START_TAG_TOKEN) {      if ($token->{type} == START_TAG_TOKEN) {
6878        if ($token->{tag_name} eq 'script') {        if ($token->{tag_name} eq 'script') {
6879            !!!cp ('t332');
6880          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6881          $script_start_tag->($insert);          $script_start_tag->();
6882          redo B;          next B;
6883        } elsif ($token->{tag_name} eq 'style') {        } elsif ($token->{tag_name} eq 'style') {
6884            !!!cp ('t333');
6885          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6886          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);          $parse_rcdata->(CDATA_CONTENT_MODEL);
6887          redo B;          next B;
6888        } elsif ({        } elsif ({
6889                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
6890                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6891            !!!cp ('t334');
6892          ## 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
6893          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6894          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
6895            !!!ack ('t334.1');
6896          !!!next-token;          !!!next-token;
6897          redo B;          next B;
6898        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
6899          ## 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
6900          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6901          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
6902    
6903          unless ($self->{confident}) {          unless ($self->{confident}) {
6904            my $charset;            if ($token->{attributes}->{charset}) {
6905            if ($token->{attributes}->{charset}) { ## TODO: And if supported              !!!cp ('t335');
6906              $charset = $token->{attributes}->{charset}->{value};              ## NOTE: Whether the encoding is supported or not is handled
6907            }              ## in the {change_encoding} callback.
6908            if ($token->{attributes}->{'http-equiv'}) {              $self->{change_encoding}
6909              ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6910              if ($token->{attributes}->{'http-equiv'}->{value}              
6911                  =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6912                      [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                  ->set_user_data (manakai_has_reference =>
6913                      ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                                       $token->{attributes}->{charset}
6914                $charset = defined $1 ? $1 : defined $2 ? $2 : $3;                                           ->{has_reference});
6915              } ## TODO: And if supported            } elsif ($token->{attributes}->{content}) {
6916                if ($token->{attributes}->{content}->{value}
6917                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6918                        [\x09\x0A\x0C\x0D\x20]*=
6919                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6920                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6921                       /x) {
6922                  !!!cp ('t336');
6923                  ## NOTE: Whether the encoding is supported or not is handled
6924                  ## in the {change_encoding} callback.
6925                  $self->{change_encoding}
6926                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6927                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6928                      ->set_user_data (manakai_has_reference =>
6929                                           $token->{attributes}->{content}
6930                                                 ->{has_reference});
6931                }
6932              }
6933            } else {
6934              if ($token->{attributes}->{charset}) {
6935                !!!cp ('t337');
6936                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6937                    ->set_user_data (manakai_has_reference =>
6938                                         $token->{attributes}->{charset}
6939                                             ->{has_reference});
6940              }
6941              if ($token->{attributes}->{content}) {
6942                !!!cp ('t338');
6943                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6944                    ->set_user_data (manakai_has_reference =>
6945                                         $token->{attributes}->{content}
6946                                             ->{has_reference});
6947            }            }
           ## TODO: Change the encoding  
6948          }          }
6949    
6950            !!!ack ('t338.1');
6951          !!!next-token;          !!!next-token;
6952          redo B;          next B;
6953        } elsif ($token->{tag_name} eq 'title') {        } elsif ($token->{tag_name} eq 'title') {
6954          !!!parse-error (type => 'in body:title');          !!!cp ('t341');
6955          ## NOTE: This is an "as if in head" code clone          ## NOTE: This is an "as if in head" code clone
6956          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6957            if (defined $self->{head_element}) {          next B;
             $self->{head_element}->append_child ($_[0]);  
           } else {  
             $insert->($_[0]);  
           }  
         });  
         redo B;  
6958        } elsif ($token->{tag_name} eq 'body') {        } elsif ($token->{tag_name} eq 'body') {
6959          !!!parse-error (type => 'in body:body');          !!!parse-error (type => 'in body', text => 'body', token => $token);
6960                                
6961          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
6962              $self->{open_elements}->[1]->[1] ne 'body') {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6963              !!!cp ('t342');
6964            ## Ignore the token            ## Ignore the token
6965          } else {          } else {
6966            my $body_el = $self->{open_elements}->[1]->[0];            my $body_el = $self->{open_elements}->[1]->[0];
6967            for my $attr_name (keys %{$token->{attributes}}) {            for my $attr_name (keys %{$token->{attributes}}) {
6968              unless ($body_el->has_attribute_ns (undef, $attr_name)) {              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6969                  !!!cp ('t343');
6970                $body_el->set_attribute_ns                $body_el->set_attribute_ns
6971                  (undef, [undef, $attr_name],                  (undef, [undef, $attr_name],
6972                   $token->{attributes}->{$attr_name}->{value});                   $token->{attributes}->{$attr_name}->{value});
6973              }              }
6974            }            }
6975          }          }
6976            !!!nack ('t343.1');
6977          !!!next-token;          !!!next-token;
6978          redo B;          next B;
6979        } elsif ({        } elsif ({
6980                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
6981                  div => 1, dl => 1, fieldset => 1, listing => 1,  
6982                  menu => 1, ol => 1, p => 1, ul => 1,                  ## NOTE: The normal one
6983                  pre => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
6984                    center => 1, datagrid => 1, details => 1, dialog => 1,
6985                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6986                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6987                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6988                    section => 1, ul => 1,
6989                    ## NOTE: As normal, but drops leading newline
6990                    pre => 1, listing => 1,
6991                    ## NOTE: As normal, but interacts with the form element pointer
6992                    form => 1,
6993                    
6994                    table => 1,
6995                    hr => 1,
6996                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
6997            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6998              !!!cp ('t350');
6999              !!!parse-error (type => 'in form:form', token => $token);
7000              ## Ignore the token
7001              !!!nack ('t350.1');
7002              !!!next-token;
7003              next B;
7004            }
7005    
7006          ## has a p element in scope          ## has a p element in scope
7007          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7008            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7009              !!!back-token;              !!!cp ('t344');
7010              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <form>
7011              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7012            } elsif ({                        line => $token->{line}, column => $token->{column}};
7013                      table => 1, caption => 1, td => 1, th => 1,              next B;
7014                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7015                     }->{$_->[1]}) {              !!!cp ('t345');
7016              last INSCOPE;              last INSCOPE;
7017            }            }
7018          } # INSCOPE          } # INSCOPE
7019                        
7020          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7021          if ($token->{tag_name} eq 'pre') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7022              !!!nack ('t346.1');
7023            !!!next-token;            !!!next-token;
7024            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
7025              $token->{data} =~ s/^\x0A//;              $token->{data} =~ s/^\x0A//;
7026              unless (length $token->{data}) {              unless (length $token->{data}) {
7027                  !!!cp ('t346');
7028                !!!next-token;                !!!next-token;
7029                } else {
7030                  !!!cp ('t349');
7031              }              }
7032              } else {
7033                !!!cp ('t348');
7034            }            }
7035          } else {          } elsif ($token->{tag_name} eq 'form') {
7036              !!!cp ('t347.1');
7037              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7038    
7039              !!!nack ('t347.2');
7040            !!!next-token;            !!!next-token;
7041          }          } elsif ($token->{tag_name} eq 'table') {
7042          redo B;            !!!cp ('t382');
7043        } elsif ($token->{tag_name} eq 'form') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7044          if (defined $self->{form_element}) {            
7045            !!!parse-error (type => 'in form:form');            $self->{insertion_mode} = IN_TABLE_IM;
7046            ## Ignore the token  
7047              !!!nack ('t382.1');
7048              !!!next-token;
7049            } elsif ($token->{tag_name} eq 'hr') {
7050              !!!cp ('t386');
7051              pop @{$self->{open_elements}};
7052            
7053              !!!nack ('t386.1');
7054            !!!next-token;            !!!next-token;
           redo B;  
7055          } else {          } else {
7056            ## 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];  
7057            !!!next-token;            !!!next-token;
           redo B;  
7058          }          }
7059            next B;
7060        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
7061          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
7062          INSCOPE: for (reverse @{$self->{open_elements}}) {  
7063            if ($_->[1] eq 'p') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7064              !!!back-token;            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7065              $token = {type => END_TAG_TOKEN, tag_name => 'p'};            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7066              redo B;            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7067            } elsif ({            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7068                      table => 1, caption => 1, td => 1, th => 1,            ## object (Fx)
7069                      button => 1, marquee => 1, object => 1, html => 1,            ## Generate non-tree (non-conforming)
7070                     }->{$_->[1]}) {            ## basefont (IE7 (where basefont is non-void)), center (IE),
7071              last INSCOPE;            ## form (IE), hn (IE)
7072            }          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7073          } # INSCOPE            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7074                        ## div (Fx, S)
7075          ## Step 1  
7076            my $non_optional;
7077          my $i = -1;          my $i = -1;
7078          my $node = $self->{open_elements}->[$i];  
7079          LI: {          ## 1.
7080            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7081            if ($node->[1] eq 'li') {            if ($node->[1] == LI_EL) {
7082              if ($i != -1) {              ## 2. (a) As if </li>
7083                !!!parse-error (type => 'end tag missing:'.              {
7084                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7085                  #
7086    
7087                  ## Otherwise
7088    
7089                  ## 1. generate implied end tags, except for </li>
7090                  #
7091    
7092                  ## 2. If current node != "li", parse error
7093                  if ($non_optional) {
7094                    !!!parse-error (type => 'not closed',
7095                                    text => $non_optional->[0]->manakai_local_name,
7096                                    token => $token);
7097                    !!!cp ('t355');
7098                  } else {
7099                    !!!cp ('t356');
7100                  }
7101    
7102                  ## 3. Pop
7103                  splice @{$self->{open_elements}}, $i;
7104              }              }
7105              splice @{$self->{open_elements}}, $i;  
7106              last LI;              last; ## 2. (b) goto 5.
7107            }            } elsif (
7108                                 ## NOTE: not "formatting" and not "phrasing"
7109            ## Step 3                     ($node->[1] & SPECIAL_EL or
7110            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7111                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7112                ($special_category->{$node->[1]} or                     (not $node->[1] & ADDRESS_DIV_P_EL)
7113                 $scoping_category->{$node->[1]}) and                    ) {
7114                $node->[1] ne 'address' and $node->[1] ne 'div') {              ## 3.
7115              last LI;              !!!cp ('t357');
7116                last; ## goto 5.
7117              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7118                !!!cp ('t358');
7119                #
7120              } else {
7121                !!!cp ('t359');
7122                $non_optional ||= $node;
7123                #
7124            }            }
7125                        ## 4.
7126            ## Step 4            ## goto 2.
7127            $i--;            $i--;
7128            $node = $self->{open_elements}->[$i];          }
7129            redo LI;  
7130          } # 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  
7131          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7132            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7133              !!!back-token;              !!!cp ('t353');
7134              $token = {type => END_TAG_TOKEN, tag_name => 'p'};  
7135              redo B;              ## NOTE: |<p><li>|, for example.
7136            } elsif ({  
7137                      table => 1, caption => 1, td => 1, th => 1,              !!!back-token; # <x>
7138                      button => 1, marquee => 1, object => 1, html => 1,              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7139                     }->{$_->[1]}) {                        line => $token->{line}, column => $token->{column}};
7140                next B;
7141              } elsif ($_->[1] & SCOPING_EL) {
7142                !!!cp ('t354');
7143              last INSCOPE;              last INSCOPE;
7144            }            }
7145          } # INSCOPE          } # INSCOPE
7146              
7147          ## Step 1          ## 5. (b) insert
7148            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7149            !!!nack ('t359.1');
7150            !!!next-token;
7151            next B;
7152          } elsif ($token->{tag_name} eq 'dt' or
7153                   $token->{tag_name} eq 'dd') {
7154            ## NOTE: As normal, but imply </dt> or </dd> when ...
7155    
7156            my $non_optional;
7157          my $i = -1;          my $i = -1;
7158          my $node = $self->{open_elements}->[$i];  
7159          LI: {          ## 1.
7160            ## Step 2          for my $node (reverse @{$self->{open_elements}}) {
7161            if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {            if ($node->[1] == DTDD_EL) {
7162              if ($i != -1) {              ## 2. (a) As if </li>
7163                !!!parse-error (type => 'end tag missing:'.              {
7164                                $self->{open_elements}->[-1]->[1]);                ## If no </li> - not applied
7165                  #
7166    
7167                  ## Otherwise
7168    
7169                  ## 1. generate implied end tags, except for </dt> or </dd>
7170                  #
7171    
7172                  ## 2. If current node != "dt"|"dd", parse error
7173                  if ($non_optional) {
7174                    !!!parse-error (type => 'not closed',
7175                                    text => $non_optional->[0]->manakai_local_name,
7176                                    token => $token);
7177                    !!!cp ('t355.1');
7178                  } else {
7179                    !!!cp ('t356.1');
7180                  }
7181    
7182                  ## 3. Pop
7183                  splice @{$self->{open_elements}}, $i;
7184              }              }
7185              splice @{$self->{open_elements}}, $i;  
7186              last LI;              last; ## 2. (b) goto 5.
7187            }            } elsif (
7188                                 ## NOTE: not "formatting" and not "phrasing"
7189            ## Step 3                     ($node->[1] & SPECIAL_EL or
7190            if (not $formatting_category->{$node->[1]} and                      $node->[1] & SCOPING_EL) and
7191                #not $phrasing_category->{$node->[1]} and                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7192                ($special_category->{$node->[1]} or  
7193                 $scoping_category->{$node->[1]}) and                     (not $node->[1] & ADDRESS_DIV_P_EL)
7194                $node->[1] ne 'address' and $node->[1] ne 'div') {                    ) {
7195              last LI;              ## 3.
7196                !!!cp ('t357.1');
7197                last; ## goto 5.
7198              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7199                !!!cp ('t358.1');
7200                #
7201              } else {
7202                !!!cp ('t359.1');
7203                $non_optional ||= $node;
7204                #
7205            }            }
7206                        ## 4.
7207            ## Step 4            ## goto 2.
7208            $i--;            $i--;
7209            $node = $self->{open_elements}->[$i];          }
7210            redo LI;  
7211          } # 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  
7212          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7213            if ($_->[1] eq 'p') {            if ($_->[1] == P_EL) {
7214              !!!back-token;              !!!cp ('t353.1');
7215              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              !!!back-token; # <x>
7216              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7217            } elsif ({                        line => $token->{line}, column => $token->{column}};
7218                      table => 1, caption => 1, td => 1, th => 1,              next B;
7219                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($_->[1] & SCOPING_EL) {
7220                     }->{$_->[1]}) {              !!!cp ('t354.1');
7221              last INSCOPE;              last INSCOPE;
7222            }            }
7223          } # INSCOPE          } # INSCOPE
7224              
7225          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          ## 5. (b) insert
7226                      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;          !!!nack ('t359.2');
             
7228          !!!next-token;          !!!next-token;
7229          redo B;          next B;
7230        } elsif ({        } elsif ($token->{tag_name} eq 'plaintext') {
7231                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          ## NOTE: As normal, but effectively ends parsing
7232                 }->{$token->{tag_name}}) {  
7233          ## has a p element in scope          ## has a p element in scope
7234          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
7235            my $node = $self->{open_elements}->[$_];            if ($_->[1] == P_EL) {
7236            if ($node->[1] eq 'p') {              !!!cp ('t367');
7237              !!!back-token;              !!!back-token; # <plaintext>
7238              $token = {type => END_TAG_TOKEN, tag_name => 'p'};              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7239              redo B;                        line => $token->{line}, column => $token->{column}};
7240            } elsif ({              next B;
7241                      table => 1, caption => 1, td => 1, th => 1,            } elsif ($_->[1] & SCOPING_EL) {
7242                      button => 1, marquee => 1, object => 1, html => 1,              !!!cp ('t368');
                    }->{$node->[1]}) {  
7243              last INSCOPE;              last INSCOPE;
7244            }            }
7245          } # INSCOPE          } # INSCOPE
7246                        
7247          ## 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;  
         #}  
7248                        
7249          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7250                        
7251            !!!nack ('t368.1');
7252          !!!next-token;          !!!next-token;
7253          redo B;          next B;
7254        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
7255          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7256            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
7257            if ($node->[1] eq 'a') {            if ($node->[1] == A_EL) {
7258              !!!parse-error (type => 'in a:a');              !!!cp ('t371');
7259                !!!parse-error (type => 'in a:a', token => $token);
7260                            
7261              !!!back-token;              !!!back-token; # <a>
7262              $token = {type => END_TAG_TOKEN, tag_name => 'a'};              $token = {type => END_TAG_TOKEN, tag_name => 'a',
7263              $formatting_end_tag->($token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7264                $formatting_end_tag->($token);
7265                            
7266              AFE2: for (reverse 0..$#$active_formatting_elements) {              AFE2: for (reverse 0..$#$active_formatting_elements) {
7267                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7268                    !!!cp ('t372');
7269                  splice @$active_formatting_elements, $_, 1;                  splice @$active_formatting_elements, $_, 1;
7270                  last AFE2;                  last AFE2;
7271                }                }
7272              } # AFE2              } # AFE2
7273              OE: for (reverse 0..$#{$self->{open_elements}}) {              OE: for (reverse 0..$#{$self->{open_elements}}) {
7274                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7275                    !!!cp ('t373');
7276                  splice @{$self->{open_elements}}, $_, 1;                  splice @{$self->{open_elements}}, $_, 1;
7277                  last OE;                  last OE;
7278                }                }
7279              } # OE              } # OE
7280              last AFE;              last AFE;
7281            } elsif ($node->[0] eq '#marker') {            } elsif ($node->[0] eq '#marker') {
7282                !!!cp ('t374');
7283              last AFE;              last AFE;
7284            }            }
7285          } # AFE          } # AFE
7286                        
7287          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7288    
7289          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7290          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7291    
7292            !!!nack ('t374.1');
7293          !!!next-token;          !!!next-token;
7294          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;  
7295        } elsif ($token->{tag_name} eq 'nobr') {        } elsif ($token->{tag_name} eq 'nobr') {
7296          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7297    
7298          ## has a |nobr| element in scope          ## has a |nobr| element in scope
7299          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7300            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7301            if ($node->[1] eq 'nobr') {            if ($node->[1] == NOBR_EL) {
7302              !!!parse-error (type => 'not closed:nobr');              !!!cp ('t376');
7303              !!!back-token;              !!!parse-error (type => 'in nobr:nobr', token => $token);
7304              $token = {type => END_TAG_TOKEN, tag_name => 'nobr'};              !!!back-token; # <nobr>
7305              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7306            } elsif ({                        line => $token->{line}, column => $token->{column}};
7307                      table => 1, caption => 1, td => 1, th => 1,              next B;
7308                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7309                     }->{$node->[1]}) {              !!!cp ('t377');
7310              last INSCOPE;              last INSCOPE;
7311            }            }
7312          } # INSCOPE          } # INSCOPE
7313                    
7314          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7315          push @$active_formatting_elements, $self->{open_elements}->[-1];          push @$active_formatting_elements, $self->{open_elements}->[-1];
7316                    
7317            !!!nack ('t377.1');
7318          !!!next-token;          !!!next-token;
7319          redo B;          next B;
7320        } elsif ($token->{tag_name} eq 'button') {        } elsif ($token->{tag_name} eq 'button') {
7321          ## has a button element in scope          ## has a button element in scope
7322          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7323            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7324            if ($node->[1] eq 'button') {            if ($node->[1] == BUTTON_EL) {
7325              !!!parse-error (type => 'in button:button');              !!!cp ('t378');
7326              !!!back-token;              !!!parse-error (type => 'in button:button', token => $token);
7327              $token = {type => END_TAG_TOKEN, tag_name => 'button'};              !!!back-token; # <button>
7328              redo B;              $token = {type => END_TAG_TOKEN, tag_name => 'button',
7329            } elsif ({                        line => $token->{line}, column => $token->{column}};
7330                      table => 1, caption => 1, td => 1, th => 1,              next B;
7331                      button => 1, marquee => 1, object => 1, html => 1,            } elsif ($node->[1] & SCOPING_EL) {
7332                     }->{$node->[1]}) {              !!!cp ('t379');
7333              last INSCOPE;              last INSCOPE;
7334            }            }
7335          } # INSCOPE          } # INSCOPE
7336                        
7337          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7338                        
7339          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7340          push @$active_formatting_elements, ['#marker', ''];  
7341            ## TODO: associate with $self->{form_element} if defined
7342    
         !!!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});  
7343          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
7344            
7345          !!!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;  
             
7346          !!!next-token;          !!!next-token;
7347          redo B;          next B;
7348        } elsif ({        } elsif ({
7349                  area => 1, basefont => 1, bgsound => 1, br => 1,                  xmp => 1,
7350                  embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                  iframe => 1,
7351                  image => 1,                  noembed => 1,
7352                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7353                    noscript => 0, ## TODO: 1 if scripting is enabled
7354                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7355          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'xmp') {
7356            !!!parse-error (type => 'image');            !!!cp ('t381');
7357            $token->{tag_name} = 'img';            $reconstruct_active_formatting_elements->($insert_to_current);
7358            } else {
7359              !!!cp ('t399');
7360          }          }
7361            ## NOTE: There is an "as if in body" code clone.
7362          ## NOTE: There is an "as if <br>" code clone.          $parse_rcdata->(CDATA_CONTENT_MODEL);
7363          $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;  
7364        } elsif ($token->{tag_name} eq 'isindex') {        } elsif ($token->{tag_name} eq 'isindex') {
7365          !!!parse-error (type => 'isindex');          !!!parse-error (type => 'isindex', token => $token);
7366                    
7367          if (defined $self->{form_element}) {          if (defined $self->{form_element}) {
7368              !!!cp ('t389');
7369            ## Ignore the token            ## Ignore the token
7370              !!!nack ('t389'); ## NOTE: Not acknowledged.
7371            !!!next-token;            !!!next-token;
7372            redo B;            next B;
7373          } else {          } else {
7374              !!!ack ('t391.1');
7375    
7376            my $at = $token->{attributes};            my $at = $token->{attributes};
7377            my $form_attrs;            my $form_attrs;
7378            $form_attrs->{action} = $at->{action} if $at->{action};            $form_attrs->{action} = $at->{action} if $at->{action};
# Line 4765  sub _tree_construction_main ($) { Line 7382  sub _tree_construction_main ($) {
7382            delete $at->{prompt};            delete $at->{prompt};
7383            my @tokens = (            my @tokens = (
7384                          {type => START_TAG_TOKEN, tag_name => 'form',                          {type => START_TAG_TOKEN, tag_name => 'form',
7385                           attributes => $form_attrs},                           attributes => $form_attrs,
7386                          {type => START_TAG_TOKEN, tag_name => 'hr'},                           line => $token->{line}, column => $token->{column}},
7387                          {type => START_TAG_TOKEN, tag_name => 'p'},                          {type => START_TAG_TOKEN, tag_name => 'hr',
7388                          {type => START_TAG_TOKEN, tag_name => 'label'},                           line => $token->{line}, column => $token->{column}},
7389                            {type => START_TAG_TOKEN, tag_name => 'p',
7390                             line => $token->{line}, column => $token->{column}},
7391                            {type => START_TAG_TOKEN, tag_name => 'label',
7392                             line => $token->{line}, column => $token->{column}},
7393                         );                         );
7394            if ($prompt_attr) {            if ($prompt_attr) {
7395              push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value}};              !!!cp ('t390');
7396                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7397                               #line => $token->{line}, column => $token->{column},
7398                              };
7399            } else {            } else {
7400                !!!cp ('t391');
7401              push @tokens, {type => CHARACTER_TOKEN,              push @tokens, {type => CHARACTER_TOKEN,
7402                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD                             data => 'This is a searchable index. Insert your search keywords here: ',
7403                               #line => $token->{line}, column => $token->{column},
7404                              }; # SHOULD
7405              ## TODO: make this configurable              ## TODO: make this configurable
7406            }            }
7407            push @tokens,            push @tokens,
7408                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at},                          {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7409                             line => $token->{line}, column => $token->{column}},
7410                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7411                          {type => END_TAG_TOKEN, tag_name => 'label'},                          {type => END_TAG_TOKEN, tag_name => 'label',
7412                          {type => END_TAG_TOKEN, tag_name => 'p'},                           line => $token->{line}, column => $token->{column}},
7413                          {type => START_TAG_TOKEN, tag_name => 'hr'},                          {type => END_TAG_TOKEN, tag_name => 'p',
7414                          {type => END_TAG_TOKEN, tag_name => 'form'};                           line => $token->{line}, column => $token->{column}},
7415            $token = shift @tokens;                          {type => START_TAG_TOKEN, tag_name => 'hr',
7416                             line => $token->{line}, column => $token->{column}},
7417                            {type => END_TAG_TOKEN, tag_name => 'form',
7418                             line => $token->{line}, column => $token->{column}};
7419            !!!back-token (@tokens);            !!!back-token (@tokens);
7420            redo B;            !!!next-token;
7421              next B;
7422          }          }
7423        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
7424          my $tag_name = $token->{tag_name};          ## Step 1
7425          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
7426                    
7427            ## Step 2
7428          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
7429    
7430            ## Step 3
7431            $self->{ignore_newline} = 1;
7432    
7433            ## Step 4
7434            ## ISSUE: This step is wrong. (r2302 enbugged)
7435    
7436            ## Step 5
7437          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7438          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
7439            
7440          $insert->($el);          ## Step 6-7
7441                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7442          my $text = '';  
7443            !!!nack ('t392.1');
7444          !!!next-token;          !!!next-token;
7445          if ($token->{type} == CHARACTER_TOKEN) {          next B;
7446            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
7447            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
7448              !!!next-token;          ## has an |option| element in scope
7449            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7450              my $node = $self->{open_elements}->[$_];
7451              if ($node->[1] == OPTION_EL) {
7452                !!!cp ('t397.1');
7453                ## NOTE: As if </option>
7454                !!!back-token; # <option> or <optgroup>
7455                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7456                          line => $token->{line}, column => $token->{column}};
7457                next B;
7458              } elsif ($node->[1] & SCOPING_EL) {
7459                !!!cp ('t397.2');
7460                last INSCOPE;
7461            }            }
7462          }          } # INSCOPE
7463          while ($token->{type} == CHARACTER_TOKEN) {  
7464            $text .= $token->{data};          $reconstruct_active_formatting_elements->($insert_to_current);
7465            !!!next-token;  
7466          }          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7467          if (length $text) {  
7468            $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});  
         }  
7469          !!!next-token;          !!!next-token;
7470          redo B;          redo B;
7471        } elsif ({        } elsif ($token->{tag_name} eq 'rt' or
7472                  iframe => 1,                 $token->{tag_name} eq 'rp') {
7473                  noembed => 1,          ## has a |ruby| element in scope
7474                  noframes => 1,          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7475                  noscript => 0, ## TODO: 1 if scripting is enabled            my $node = $self->{open_elements}->[$_];
7476                 }->{$token->{tag_name}}) {            if ($node->[1] == RUBY_EL) {
7477          ## NOTE: There are two "as if in body" code clones.              !!!cp ('t398.1');
7478          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);              ## generate implied end tags
7479                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7480                  !!!cp ('t398.2');
7481                  pop @{$self->{open_elements}};
7482                }
7483                unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7484                  !!!cp ('t398.3');
7485                  !!!parse-error (type => 'not closed',
7486                                  text => $self->{open_elements}->[-1]->[0]
7487                                      ->manakai_local_name,
7488                                  token => $token);
7489                  pop @{$self->{open_elements}}
7490                      while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
7491                }
7492                last INSCOPE;
7493              } elsif ($node->[1] & SCOPING_EL) {
7494                !!!cp ('t398.4');
7495                last INSCOPE;
7496              }
7497            } # INSCOPE
7498    
7499            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7500    
7501            !!!nack ('t398.5');
7502            !!!next-token;
7503          redo B;          redo B;
7504        } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'math' or
7505                   $token->{tag_name} eq 'svg') {
7506          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7507    
7508            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7509    
7510            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7511    
7512            ## "adjust foreign attributes" - done in insert-element-f
7513                    
7514          !!!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);
7515                    
7516          $self->{insertion_mode} = IN_SELECT_IM;          if ($self->{self_closing}) {
7517              pop @{$self->{open_elements}};
7518              !!!ack ('t398.6');
7519            } else {
7520              !!!cp ('t398.7');
7521              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7522              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7523              ## mode, "in body" (not "in foreign content") secondary insertion
7524              ## mode, maybe.
7525            }
7526    
7527          !!!next-token;          !!!next-token;
7528          redo B;          next B;
7529        } elsif ({        } elsif ({
7530                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
7531                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
7532                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
7533                  thead => 1, tr => 1,                  thead => 1, tr => 1,
7534                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7535          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!cp ('t401');
7536            !!!parse-error (type => 'in body',
7537                            text => $token->{tag_name}, token => $token);
7538          ## Ignore the token          ## Ignore the token
7539            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7540            !!!next-token;
7541            next B;
7542          } elsif ($token->{tag_name} eq 'param' or
7543                   $token->{tag_name} eq 'source') {
7544            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7545            pop @{$self->{open_elements}};
7546    
7547            !!!ack ('t398.5');
7548          !!!next-token;          !!!next-token;
7549          redo B;          redo B;
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
7550        } else {        } else {
7551            if ($token->{tag_name} eq 'image') {
7552              !!!cp ('t384');
7553              !!!parse-error (type => 'image', token => $token);
7554              $token->{tag_name} = 'img';
7555            } else {
7556              !!!cp ('t385');
7557            }
7558    
7559            ## NOTE: There is an "as if <br>" code clone.
7560          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7561                    
7562          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7563    
7564            if ({
7565                 applet => 1, marquee => 1, object => 1,
7566                }->{$token->{tag_name}}) {
7567              !!!cp ('t380');
7568              push @$active_formatting_elements, ['#marker', ''];
7569              !!!nack ('t380.1');
7570            } elsif ({
7571                      b => 1, big => 1, em => 1, font => 1, i => 1,
7572                      s => 1, small => 1, strike => 1,
7573                      strong => 1, tt => 1, u => 1,
7574                     }->{$token->{tag_name}}) {
7575              !!!cp ('t375');
7576              push @$active_formatting_elements, $self->{open_elements}->[-1];
7577              !!!nack ('t375.1');
7578            } elsif ($token->{tag_name} eq 'input') {
7579              !!!cp ('t388');
7580              ## TODO: associate with $self->{form_element} if defined
7581              pop @{$self->{open_elements}};
7582              !!!ack ('t388.2');
7583            } elsif ({
7584                      area => 1, basefont => 1, bgsound => 1, br => 1,
7585                      embed => 1, img => 1, spacer => 1, wbr => 1,
7586                     }->{$token->{tag_name}}) {
7587              !!!cp ('t388.1');
7588              pop @{$self->{open_elements}};
7589              !!!ack ('t388.3');
7590            } elsif ($token->{tag_name} eq 'select') {
7591              ## TODO: associate with $self->{form_element} if defined
7592            
7593              if ($self->{insertion_mode} & TABLE_IMS or
7594                  $self->{insertion_mode} & BODY_TABLE_IMS or
7595                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7596                !!!cp ('t400.1');
7597                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7598              } else {
7599                !!!cp ('t400.2');
7600                $self->{insertion_mode} = IN_SELECT_IM;
7601              }
7602              !!!nack ('t400.3');
7603            } else {
7604              !!!nack ('t402');
7605            }
7606                    
7607          !!!next-token;          !!!next-token;
7608          redo B;          next B;
7609        }        }
7610      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7611        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
7612          if (@{$self->{open_elements}} > 1 and          ## has a |body| element in scope
7613              $self->{open_elements}->[1]->[1] eq 'body') {          my $i;
7614            for (@{$self->{open_elements}}) {          INSCOPE: {
7615              unless ({            for (reverse @{$self->{open_elements}}) {
7616                         dd => 1, dt => 1, li => 1, p => 1, td => 1,              if ($_->[1] == BODY_EL) {
7617                         th => 1, tr => 1, body => 1, html => 1,                !!!cp ('t405');
7618                       tbody => 1, tfoot => 1, thead => 1,                $i = $_;
7619                      }->{$_->[1]}) {                last INSCOPE;
7620                !!!parse-error (type => 'not closed:'.$_->[1]);              } elsif ($_->[1] & SCOPING_EL) {
7621                  !!!cp ('t405.1');
7622                  last;
7623              }              }
7624            }            }
7625    
7626            $self->{insertion_mode} = AFTER_BODY_IM;            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7627            !!!next-token;  
7628            redo B;            !!!parse-error (type => 'unmatched end tag',
7629          } else {                            text => $token->{tag_name}, token => $token);
7630            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: Ignore the token.
           ## Ignore the token  
7631            !!!next-token;            !!!next-token;
7632            redo B;            next B;
7633            } # INSCOPE
7634    
7635            for (@{$self->{open_elements}}) {
7636              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7637                !!!cp ('t403');
7638                !!!parse-error (type => 'not closed',
7639                                text => $_->[0]->manakai_local_name,
7640                                token => $token);
7641                last;
7642              } else {
7643                !!!cp ('t404');
7644              }
7645          }          }
7646    
7647            $self->{insertion_mode} = AFTER_BODY_IM;
7648            !!!next-token;
7649            next B;
7650        } elsif ($token->{tag_name} eq 'html') {        } elsif ($token->{tag_name} eq 'html') {
7651          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
7652            ## ISSUE: There is an issue in the spec.          ## up-to-date, though it has same effect as speced.
7653            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if (@{$self->{open_elements}} > 1 and
7654              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);              $self->{open_elements}->[1]->[1] == BODY_EL) {
7655              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7656                !!!cp ('t406');
7657                !!!parse-error (type => 'not closed',
7658                                text => $self->{open_elements}->[1]->[0]
7659                                    ->manakai_local_name,
7660                                token => $token);
7661              } else {
7662                !!!cp ('t407');
7663            }            }
7664            $self->{insertion_mode} = AFTER_BODY_IM;            $self->{insertion_mode} = AFTER_BODY_IM;
7665            ## reprocess            ## reprocess
7666            redo B;            next B;
7667          } else {          } else {
7668            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t408');
7669              !!!parse-error (type => 'unmatched end tag',
7670                              text => $token->{tag_name}, token => $token);
7671            ## Ignore the token            ## Ignore the token
7672            !!!next-token;            !!!next-token;
7673            redo B;            next B;
7674          }          }
7675        } elsif ({        } elsif ({
7676                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
7677                  div => 1, dl => 1, fieldset => 1, listing => 1,  
7678                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
7679                  p => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
7680                    center => 1, datagrid => 1, details => 1, dialog => 1,
7681                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7682                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7683                    ol => 1, pre => 1, section => 1, ul => 1,
7684    
7685                    ## NOTE: As normal, but ... optional tags
7686                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
7687                  button => 1, marquee => 1, object => 1,  
7688                    applet => 1, button => 1, marquee => 1, object => 1,
7689                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7690            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7691            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7692            ## </dd>" code.
7693    
7694          ## has an element in scope          ## has an element in scope
7695          my $i;          my $i;
7696          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7697            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7698            if ($node->[1] eq $token->{tag_name}) {            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7699              ## 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;  
             }  
7700              $i = $_;              $i = $_;
7701              last INSCOPE unless $token->{tag_name} eq 'p';              last INSCOPE;
7702            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7703                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t411');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7704              last INSCOPE;              last INSCOPE;
7705            }            }
7706          } # INSCOPE          } # INSCOPE
7707            
7708          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7709            if (defined $i) {            !!!cp ('t413');
7710              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!parse-error (type => 'unmatched end tag',
7711                              text => $token->{tag_name}, token => $token);
7712              ## NOTE: Ignore the token.
7713            } else {
7714              ## Step 1. generate implied end tags
7715              while ({
7716                      ## END_TAG_OPTIONAL_EL
7717                      dd => ($token->{tag_name} ne 'dd'),
7718                      dt => ($token->{tag_name} ne 'dt'),
7719                      li => ($token->{tag_name} ne 'li'),
7720                      option => 1,
7721                      optgroup => 1,
7722                      p => 1,
7723                      rt => 1,
7724                      rp => 1,
7725                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7726                !!!cp ('t409');
7727                pop @{$self->{open_elements}};
7728              }
7729    
7730              ## Step 2.
7731              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7732                      ne $token->{tag_name}) {
7733                !!!cp ('t412');
7734                !!!parse-error (type => 'not closed',
7735                                text => $self->{open_elements}->[-1]->[0]
7736                                    ->manakai_local_name,
7737                                token => $token);
7738            } else {            } else {
7739              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t414');
7740            }            }
7741          }  
7742                      ## Step 3.
         if (defined $i) {  
7743            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
7744          } elsif ($token->{tag_name} eq 'p') {  
7745            ## As if <p>, then reprocess the current token            ## Step 4.
7746            my $el;            $clear_up_to_marker->()
7747            !!!create-element ($el, 'p');                if {
7748            $insert->($el);                  applet => 1, button => 1, marquee => 1, object => 1,
7749                  }->{$token->{tag_name}};
7750          }          }
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
7751          !!!next-token;          !!!next-token;
7752          redo B;          next B;
7753        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
7754            ## NOTE: As normal, but interacts with the form element pointer
7755    
7756            undef $self->{form_element};
7757    
7758          ## has an element in scope          ## has an element in scope
7759            my $i;
7760          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7761            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7762            if ($node->[1] eq $token->{tag_name}) {            if ($node->[1] == FORM_EL) {
7763              ## generate implied end tags              !!!cp ('t418');
7764              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;  
             }  
7765              last INSCOPE;              last INSCOPE;
7766            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7767                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t419');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7768              last INSCOPE;              last INSCOPE;
7769            }            }
7770          } # INSCOPE          } # INSCOPE
7771            
7772          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7773            pop @{$self->{open_elements}};            !!!cp ('t421');
7774              !!!parse-error (type => 'unmatched end tag',
7775                              text => $token->{tag_name}, token => $token);
7776              ## NOTE: Ignore the token.
7777          } else {          } else {
7778            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            ## Step 1. generate implied end tags
7779              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7780                !!!cp ('t417');
7781                pop @{$self->{open_elements}};
7782              }
7783              
7784              ## Step 2.
7785              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7786                      ne $token->{tag_name}) {
7787                !!!cp ('t417.1');
7788                !!!parse-error (type => 'not closed',
7789                                text => $self->{open_elements}->[-1]->[0]
7790                                    ->manakai_local_name,
7791                                token => $token);
7792              } else {
7793                !!!cp ('t420');
7794              }  
7795              
7796              ## Step 3.
7797              splice @{$self->{open_elements}}, $i;
7798          }          }
7799    
         undef $self->{form_element};  
7800          !!!next-token;          !!!next-token;
7801          redo B;          next B;
7802        } elsif ({        } elsif ({
7803                    ## NOTE: As normal, except acts as a closer for any ...
7804                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7805                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7806          ## has an element in scope          ## has an element in scope
7807          my $i;          my $i;
7808          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7809            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
7810            if ({            if ($node->[1] == HEADING_EL) {
7811                 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;  
             }  
7812              $i = $_;              $i = $_;
7813              last INSCOPE;              last INSCOPE;
7814            } elsif ({            } elsif ($node->[1] & SCOPING_EL) {
7815                      table => 1, caption => 1, td => 1, th => 1,              !!!cp ('t424');
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
7816              last INSCOPE;              last INSCOPE;
7817            }            }
7818          } # INSCOPE          } # INSCOPE
7819            
7820          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {          unless (defined $i) { # has an element in scope
7821            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            !!!cp ('t425.1');
7822              !!!parse-error (type => 'unmatched end tag',
7823                              text => $token->{tag_name}, token => $token);
7824              ## NOTE: Ignore the token.
7825            } else {
7826              ## Step 1. generate implied end tags
7827              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7828                !!!cp ('t422');
7829                pop @{$self->{open_elements}};
7830              }
7831              
7832              ## Step 2.
7833              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7834                      ne $token->{tag_name}) {
7835                !!!cp ('t425');
7836                !!!parse-error (type => 'unmatched end tag',
7837                                text => $token->{tag_name}, token => $token);
7838              } else {
7839                !!!cp ('t426');
7840              }
7841    
7842              ## Step 3.
7843              splice @{$self->{open_elements}}, $i;
7844          }          }
7845                    
         splice @{$self->{open_elements}}, $i if defined $i;  
7846          !!!next-token;          !!!next-token;
7847          redo B;          next B;
7848          } elsif ($token->{tag_name} eq 'p') {
7849            ## NOTE: As normal, except </p> implies <p> and ...
7850    
7851            ## has an element in scope
7852            my $non_optional;
7853            my $i;
7854            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7855              my $node = $self->{open_elements}->[$_];
7856              if ($node->[1] == P_EL) {
7857                !!!cp ('t410.1');
7858                $i = $_;
7859                last INSCOPE;
7860              } elsif ($node->[1] & SCOPING_EL) {
7861                !!!cp ('t411.1');
7862                last INSCOPE;
7863              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7864                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7865                !!!cp ('t411.2');
7866                #
7867              } else {
7868                !!!cp ('t411.3');
7869                $non_optional ||= $node;
7870                #
7871              }
7872            } # INSCOPE
7873    
7874            if (defined $i) {
7875              ## 1. Generate implied end tags
7876              #
7877    
7878              ## 2. If current node != "p", parse error
7879              if ($non_optional) {
7880                !!!cp ('t412.1');
7881                !!!parse-error (type => 'not closed',
7882                                text => $non_optional->[0]->manakai_local_name,
7883                                token => $token);
7884              } else {
7885                !!!cp ('t414.1');
7886              }
7887    
7888              ## 3. Pop
7889              splice @{$self->{open_elements}}, $i;
7890            } else {
7891              !!!cp ('t413.1');
7892              !!!parse-error (type => 'unmatched end tag',
7893                              text => $token->{tag_name}, token => $token);
7894    
7895              !!!cp ('t415.1');
7896              ## As if <p>, then reprocess the current token
7897              my $el;
7898              !!!create-element ($el, $HTML_NS, 'p',, $token);
7899              $insert->($el);
7900              ## NOTE: Not inserted into |$self->{open_elements}|.
7901            }
7902    
7903            !!!next-token;
7904            next B;
7905        } elsif ({        } elsif ({
7906                  a => 1,                  a => 1,
7907                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
7908                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
7909                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
7910                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
7911          $formatting_end_tag->($token->{tag_name});          !!!cp ('t427');
7912          redo B;          $formatting_end_tag->($token);
7913            next B;
7914        } elsif ($token->{tag_name} eq 'br') {        } elsif ($token->{tag_name} eq 'br') {
7915          !!!parse-error (type => 'unmatched end tag:br');          !!!cp ('t428');
7916            !!!parse-error (type => 'unmatched end tag',
7917                            text => 'br', token => $token);
7918    
7919          ## As if <br>          ## As if <br>
7920          $reconstruct_active_formatting_elements->($insert_to_current);          $reconstruct_active_formatting_elements->($insert_to_current);
7921                    
7922          my $el;          my $el;
7923          !!!create-element ($el, 'br');          !!!create-element ($el, $HTML_NS, 'br',, $token);
7924          $insert->($el);          $insert->($el);
7925                    
7926          ## Ignore the token.          ## Ignore the token.
7927          !!!next-token;          !!!next-token;
7928          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  
           
7929        } else {        } else {
7930            if ($token->{tag_name} eq 'sarcasm') {
7931              sleep 0.001; # take a deep breath
7932            }
7933    
7934          ## Step 1          ## Step 1
7935          my $node_i = -1;          my $node_i = -1;
7936          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
7937    
7938          ## Step 2          ## Step 2
7939          S2: {          S2: {
7940            if ($node->[1] eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
7941              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7942              if ($node_tag_name eq $token->{tag_name}) {
7943              ## Step 1              ## Step 1
7944              ## generate implied end tags              ## generate implied end tags
7945              if ({              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7946                   dd => 1, dt => 1, li => 1, p => 1,                !!!cp ('t430');
7947                   td => 1, th => 1, tr => 1,                ## NOTE: |<ruby><rt></ruby>|.
7948                   tbody => 1, tfoot => 1, thead => 1,                ## ISSUE: <ruby><rt></rt> will also take this code path,
7949                  }->{$self->{open_elements}->[-1]->[1]}) {                ## which seems wrong.
7950                !!!back-token;                pop @{$self->{open_elements}};
7951                $token = {type => END_TAG_TOKEN,                $node_i++;
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               redo B;  
7952              }              }
7953                    
7954              ## Step 2              ## Step 2
7955              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              my $current_tag_name
7956                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7957                $current_tag_name =~ tr/A-Z/a-z/;
7958                if ($current_tag_name ne $token->{tag_name}) {
7959                  !!!cp ('t431');
7960                  ## NOTE: <x><y></x>
7961                  !!!parse-error (type => 'not closed',
7962                                  text => $self->{open_elements}->[-1]->[0]
7963                                      ->manakai_local_name,
7964                                  token => $token);
7965                } else {
7966                  !!!cp ('t432');
7967              }              }
7968                            
7969              ## Step 3              ## Step 3
7970              splice @{$self->{open_elements}}, $node_i;              splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7971    
7972              !!!next-token;              !!!next-token;
7973              last S2;              last S2;
7974            } else {            } else {
7975              ## Step 3              ## Step 3
7976              if (not $formatting_category->{$node->[1]} and              if (not ($node->[1] & FORMATTING_EL) and
7977                  #not $phrasing_category->{$node->[1]} and                  #not $phrasing_category->{$node->[1]} and
7978                  ($special_category->{$node->[1]} or                  ($node->[1] & SPECIAL_EL or
7979                   $scoping_category->{$node->[1]})) {                   $node->[1] & SCOPING_EL)) {
7980                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t433');
7981                  !!!parse-error (type => 'unmatched end tag',
7982                                  text => $token->{tag_name}, token => $token);
7983                ## Ignore the token                ## Ignore the token
7984                !!!next-token;                !!!next-token;
7985                last S2;                last S2;
7986    
7987                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7988                  ## 9.27, "a" is a child of <dd> (conforming).  In
7989                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7990                  ## "a" is a child of both <body> and <dd>.
7991              }              }
7992                
7993                !!!cp ('t434');
7994            }            }
7995                        
7996            ## Step 4            ## Step 4
# Line 5122  sub _tree_construction_main ($) { Line 8000  sub _tree_construction_main ($) {
8000            ## Step 5;            ## Step 5;
8001            redo S2;            redo S2;
8002          } # S2          } # S2
8003          redo B;          next B;
8004        }        }
8005      }      }
8006      redo B;      next B;
8007      } continue { # B
8008        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8009          ## NOTE: The code below is executed in cases where it does not have
8010          ## to be, but it it is harmless even in those cases.
8011          ## has an element in scope
8012          INSCOPE: {
8013            for (reverse 0..$#{$self->{open_elements}}) {
8014              my $node = $self->{open_elements}->[$_];
8015              if ($node->[1] & FOREIGN_EL) {
8016                last INSCOPE;
8017              } elsif ($node->[1] & SCOPING_EL) {
8018                last;
8019              }
8020            }
8021            
8022            ## NOTE: No foreign element in scope.
8023            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8024          } # INSCOPE
8025        }
8026    } # B    } # B
8027    
   ## 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  
   
8028    ## Stop parsing # MUST    ## Stop parsing # MUST
8029        
8030    ## TODO: script stuffs    ## TODO: script stuffs
8031  } # _tree_construct_main  } # _tree_construct_main
8032    
8033  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8034    my $class = shift;    my $class = shift;
8035    my $node = shift;    my $node = shift;
8036    my $s = \$_[0];    #my $s = \$_[0];
8037    my $onerror = $_[1];    my $onerror = $_[1];
8038      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8039    
8040      ## ISSUE: Should {confident} be true?
8041    
8042    my $nt = $node->node_type;    my $nt = $node->node_type;
8043    if ($nt == 9) {    if ($nt == 9) {
# Line 5159  sub set_inner_html ($$$) { Line 8054  sub set_inner_html ($$$) {
8054      }      }
8055    
8056      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8057      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8058    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8059      ## TODO: If non-html element      ## TODO: If non-html element
8060    
8061      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8062    
8063    ## TODO: Support for $get_wrapper
8064    
8065      ## Step 1 # MUST      ## Step 1 # MUST
8066      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8067      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5172  sub set_inner_html ($$$) { Line 8069  sub set_inner_html ($$$) {
8069      my $p = $class->new;      my $p = $class->new;
8070      $p->{document} = $doc;      $p->{document} = $doc;
8071    
8072      ## Step 9 # MUST      ## Step 8 # MUST
8073      my $i = 0;      my $i = 0;
8074      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8075      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8076      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8077        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8078        $input = $get_wrapper->($input);
8079        $p->{set_nc} = sub {
8080        my $self = shift;        my $self = shift;
8081    
8082        pop @{$self->{prev_input_character}};        my $char = '';
8083        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8084            $char = $self->{next_nc};
8085            delete $self->{next_nc};
8086            $self->{nc} = ord $char;
8087          } else {
8088            $self->{char_buffer} = '';
8089            $self->{char_buffer_pos} = 0;
8090            
8091            my $count = $input->manakai_read_until
8092                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8093                 $self->{char_buffer_pos});
8094            if ($count) {
8095              $self->{line_prev} = $self->{line};
8096              $self->{column_prev} = $self->{column};
8097              $self->{column}++;
8098              $self->{nc}
8099                  = ord substr ($self->{char_buffer},
8100                                $self->{char_buffer_pos}++, 1);
8101              return;
8102            }
8103            
8104            if ($input->read ($char, 1)) {
8105              $self->{nc} = ord $char;
8106            } else {
8107              $self->{nc} = -1;
8108              return;
8109            }
8110          }
8111    
8112          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8113          $p->{column}++;
8114    
8115        $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
8116        $self->{next_input_character} = ord substr $$s, $i++, 1;          $p->{line}++;
8117        $column++;          $p->{column} = 0;
8118            !!!cp ('i1');
8119        if ($self->{next_input_character} == 0x000A) { # LF        } elsif ($self->{nc} == 0x000D) { # CR
8120          $line++;  ## TODO: support for abort/streaming
8121          $column = 0;          my $next = '';
8122        } elsif ($self->{next_input_character} == 0x000D) { # CR          if ($input->read ($next, 1) and $next ne "\x0A") {
8123          $i++ if substr ($$s, $i, 1) eq "\x0A";            $self->{next_nc} = $next;
8124          $self->{next_input_character} = 0x000A; # LF # MUST          }
8125          $line++;          $self->{nc} = 0x000A; # LF # MUST
8126          $column = 0;          $p->{line}++;
8127        } elsif ($self->{next_input_character} > 0x10FFFF) {          $p->{column} = 0;
8128          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          !!!cp ('i2');
8129        } elsif ($self->{next_input_character} == 0x0000) { # NULL        } elsif ($self->{nc} == 0x0000) { # NULL
8130            !!!cp ('i4');
8131          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8132          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8133        }        }
8134      };      };
8135      $p->{prev_input_character} = [-1, -1, -1];  
8136      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8137              #my ($scalar, $specials_range, $offset) = @_;
8138          return 0 if defined $p->{next_nc};
8139    
8140          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8141          my $offset = $_[2] || 0;
8142          
8143          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8144            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8145            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8146              substr ($_[0], $offset)
8147                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8148              my $count = $+[0] - $-[0];
8149              if ($count) {
8150                $p->{column} += $count;
8151                $p->{char_buffer_pos} += $count;
8152                $p->{line_prev} = $p->{line};
8153                $p->{column_prev} = $p->{column} - 1;
8154                $p->{nc} = -1;
8155              }
8156              return $count;
8157            } else {
8158              return 0;
8159            }
8160          } else {
8161            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8162            if ($count) {
8163              $p->{column} += $count;
8164              $p->{column_prev} += $count;
8165              $p->{nc} = -1;
8166            }
8167            return $count;
8168          }
8169        }; # $p->{read_until}
8170    
8171      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8172        my (%opt) = @_;        my (%opt) = @_;
8173        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8174          my $column = $opt{column};
8175          if (defined $opt{token} and defined $opt{token}->{line}) {
8176            $line = $opt{token}->{line};
8177            $column = $opt{token}->{column};
8178          }
8179          warn "Parse error ($opt{type}) at line $line column $column\n";
8180      };      };
8181      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8182        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8183      };      };
8184            
8185        my $char_onerror = sub {
8186          my (undef, $type, %opt) = @_;
8187          $ponerror->(layer => 'encode',
8188                      line => $p->{line}, column => $p->{column} + 1,
8189                      %opt, type => $type);
8190        }; # $char_onerror
8191        $input->onerror ($char_onerror);
8192    
8193      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8194      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8195    
8196      ## Step 2      ## Step 2
8197      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8198      $p->{content_model} = {      $p->{content_model} = {
8199        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
8200        textarea => RCDATA_CONTENT_MODEL,        textarea => RCDATA_CONTENT_MODEL,
# Line 5233  sub set_inner_html ($$$) { Line 8211  sub set_inner_html ($$$) {
8211          unless defined $p->{content_model};          unless defined $p->{content_model};
8212          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8213    
8214      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8215          ## TODO: Foreign element OK?
8216    
8217      ## Step 4      ## Step 3
8218      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8219        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8220    
8221      ## Step 5 # MUST      ## Step 4 # MUST
8222      $doc->append_child ($root);      $doc->append_child ($root);
8223    
8224      ## Step 6 # MUST      ## Step 5 # MUST
8225      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8226    
8227      undef $p->{head_element};      undef $p->{head_element};
8228        undef $p->{head_element_inserted};
8229    
8230      ## Step 7 # MUST      ## Step 6 # MUST
8231      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8232    
8233      ## Step 8 # MUST      ## Step 7 # MUST
8234      my $anode = $node;      my $anode = $node;
8235      AN: while (defined $anode) {      AN: while (defined $anode) {
8236        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8237          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8238          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8239            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8240                !!!cp ('i5');
8241              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8242              last AN;              last AN;
8243            }            }
# Line 5265  sub set_inner_html ($$$) { Line 8246  sub set_inner_html ($$$) {
8246        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8247      } # AN      } # AN
8248            
8249      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8250      {      {
8251        my $self = $p;        my $self = $p;
8252        !!!next-token;        !!!next-token;
8253      }      }
8254      $p->_tree_construction_main;      $p->_tree_construction_main;
8255    
8256      ## Step 11 # MUST      ## Step 10 # MUST
8257      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8258      for (@cn) {      for (@cn) {
8259        $node->remove_child ($_);        $node->remove_child ($_);
8260      }      }
8261      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8262    
8263      ## Step 12 # MUST      ## Step 11 # MUST
8264      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8265      for (@cn) {      for (@cn) {
8266        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5289  sub set_inner_html ($$$) { Line 8269  sub set_inner_html ($$$) {
8269      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8270    
8271      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8272    
8273        delete $p->{parse_error}; # delete loop
8274    } else {    } else {
8275      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";
8276    }    }
# Line 5296  sub set_inner_html ($$$) { Line 8278  sub set_inner_html ($$$) {
8278    
8279  } # tree construction stage  } # tree construction stage
8280    
8281  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8282    my (undef, $node, $on_error) = @_;  push our @ISA, 'Error';
   
   ## Step 1  
   my $s = '';  
   
   my $in_cdata;  
   my $parent = $node;  
   while (defined $parent) {  
     if ($parent->node_type == 1 and  
         $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and  
         {  
           style => 1, script => 1, xmp => 1, iframe => 1,  
           noembed => 1, noframes => 1, noscript => 1,  
         }->{$parent->local_name}) { ## TODO: case thingy  
       $in_cdata = 1;  
     }  
     $parent = $parent->parent_node;  
   }  
   
   ## Step 2  
   my @node = @{$node->child_nodes};  
   C: while (@node) {  
     my $child = shift @node;  
     unless (ref $child) {  
       if ($child eq 'cdata-out') {  
         $in_cdata = 0;  
       } else {  
         $s .= $child; # end tag  
       }  
       next C;  
     }  
       
     my $nt = $child->node_type;  
     if ($nt == 1) { # Element  
       my $tag_name = $child->tag_name; ## TODO: manakai_tag_name  
       $s .= '<' . $tag_name;  
       ## NOTE: Non-HTML case:  
       ## <http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/11191>  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = $attr->name; ## TODO: manakai_name  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       $s .= "\x0A" if $tag_name eq 'pre' or $tag_name eq 'textarea';  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
         plaintext => 1,  
       }->{$tag_name}) {  
         unshift @node, 'cdata-out';  
         $in_cdata = 1;  
       }  
   
       unshift @node, @{$child->child_nodes}, '</' . $tag_name . '>';  
     } elsif ($nt == 3 or $nt == 4) {  
       if ($in_cdata) {  
         $s .= $child->data;  
       } else {  
         my $value = $child->data;  
         $value =~ s/&/&amp;/g;  
         $value =~ s/</&lt;/g;  
         $value =~ s/>/&gt;/g;  
         $value =~ s/"/&quot;/g;  
         $s .= $value;  
       }  
     } elsif ($nt == 8) {  
       $s .= '<!--' . $child->data . '-->';  
     } elsif ($nt == 10) {  
       $s .= '<!DOCTYPE ' . $child->name . '>';  
     } elsif ($nt == 5) { # entrefs  
       push @node, @{$child->child_nodes};  
     } else {  
       $on_error->($child) if defined $on_error;  
     }  
     ## ISSUE: This code does not support PIs.  
   } # C  
     
   ## Step 3  
   return \$s;  
 } # get_inner_html  
8283    
8284  1;  1;
8285  # $Date$  # $Date$

Legend:
Removed from v.1.56  
changed lines
  Added in v.1.207

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24